About stigg-contracts
stigg-contracts is a skill for handling Stigg contracts, covering subscription entitlements, billing contracts, invoices, and contract changes. It distinguishes the roles of contracts, subscriptions, and billing terms, and guides operations through REST or GraphQL.
Key features
- 01Create contracts and manage subscription links
- 02Configure billing terms and priced items
- 03Check billing totals and invoice schedules
- 04Handle invoices and credit notes
- 05Explain contract lifecycle constraints
Use cases
- 01Set up subscription entitlements for a sales contract
- 02Configure billing and invoices from order terms
- 03Amend active contracts or handle billing changes
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ stiggio/ skills --skill stigg-contractsAfter installing, type/stigg-contractsin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
references/billing-and-invoices.mdcontract-model.mdpricing-models.mdDetails
From the project READMEStigg Contracts — Entitlements and Invoicing
A contract is the source of truth for a sales-led deal: what the customer can do, and what you charge for it. It groups subscriptions for one customer, and those subscriptions grant the entitlements — the contract is the agreement they belong to, not a second entitlement mechanism. It can also carry a billing contract, which produces invoices. The halves are independent — a contract with no billing contract still provisions access.
The contract itself holds no commercial terms. No price, quantity or commitment fields — only a name, PO number, activation window, and its subscription links. Negotiated price and committed volumes live on the custom subscription (
priceOverrides,charges,unitQuantity,minimumSpend—stigg-subscriptions). Priced line items exist only on the billing half, below.
Before You Start
Per the umbrella stigg skill: search first. Confirm shapes and field names against the docs.
No SDK surface. Contracts are not in
@stigg/node-server-sdkor@stigg/js-client-sdk. Use REST (canonical here,https://api.stigg.io/api/v1, headerX-API-KEY) or GraphQL.
Availability. Not enabled for every account. If the endpoints 404, it isn't turned on — ask Stigg rather than working around it.
How would you like to start?
The dashboard asks exactly this, with two independent checkboxes — Provision access and Set up billing — so there are three ways to start. Establish which first; it decides every step below.
| Choice | What the contract does | Send |
|---|---|---|
| Provisioning only | Groups custom subscriptions and grants their entitlements. No invoices. | setupBilling: false + subscriptions |
| Billing only | Priced line items and invoices, no subscriptions attached. | setupBilling: true, no subscriptions |
| Both | Entitlements and invoicing on one agreement — the usual shape for a signed order form. | setupBilling: true + subscriptions |
setupBilling defaults to true, so provisioning-only must be asked for explicitly.
Vocabulary — recognize every name, say only one. UI: "Provision access" / "Set up billing". Docs: "Entitlement provisioning" / "Billing contract" / "Both". API:
setupBilling.Never show the user a field name —
setupBillingincluded. Say "provisioning only", "billing only" or "both". That holds for every field below: the user confirms the deal in the words of their order form, not in JSON.
The Operation Map
| Op | Purpose | Reference |
|---|---|---|
| Create, attach, detach, amend, archive, read | The contract and its membership | inline below |
| Set up billing | Terms, settings, priced line items, totals, publish | below + references/billing-and-invoices.md |
| Pick a pricing model | Deal shape → model + rows, and the shapes with no model | references/pricing-models.md |
| Invoices | List, filter, one invoice, the PDF, mark paid | references/billing-and-invoices.md |
| Credit notes | Settle a change once a contract can no longer be edited | references/billing-and-invoices.md |
Provision Access
Group custom subscriptions and grant their entitlements. No invoices unless billing is also set up.
Prerequisites
Only custom plans qualify — custom plans and their add-ons, not self-service ones. Every subscription
must be custom-priced: a CUSTOM plan, or a PAID plan provisioned with paymentCollectionMethod: "NONE". A FREE-plan subscription can never join (stigg-pricing-modeling).
Provision the customer's account first — create the contract only once the customer exists.
Three server-side rules: one subscription per product, one currency, and every subscription belongs to the contract's customer.
Both: the order to do it in
From an order form, create the contract first and let it provision the subscriptions — provisioning
standalone leaves one live and unattached until a second write links it. Both contract-first shapes are one
transaction: POST /contracts with newSubscription entries, or the same entries on
POST /contracts/:id/subscriptions later.
So: elicit → create the contract with its subscriptions → verify entitlements → price the lines.
Create the Contract
setupBilling: false is the difference — and it defaults to true, so send it explicitly.
curl -X POST "https://api.stigg.io/api/v1/contracts" \
-H "X-API-KEY: <YOUR_API_KEY>" -H "content-type: application/json" \
-d '{
"customerId": "customer-acme",
"contractId": "contract-acme-po-4821",
"name": "Acme 2026",
"poNumber": "PO-4821",
"setupBilling": false,
"subscriptions": [ { "existingSubscriptionId": "subscription-acme-platform" } ]
}'
Each entry is exactly one of existingSubscriptionId or newSubscription — the full
provision-subscription body with its own customerId and planId (stigg-subscriptions). Only
customerId is required; omit subscriptions for an empty contract. The create is atomic — a failure
leaves nothing behind.
Always send your own contractId — the idempotency key, derived from the deal (the PO or order
number). The same one again returns the existing contract; without it a retry silently duplicates, and
contracts cannot be deleted.
Attach and Detach Subscriptions
C=https://api.stigg.io/api/v1/contracts/contract-acme-2026
# Attach existing ones; `subscriptions` with create's own entries provisions new ones instead
curl -X POST "$C/subscriptions" -H "X-API-KEY: <KEY>" -H "content-type: application/json" \
-d '{ "subscriptionIds": ["subscription-acme-analytics"] }'
# Detach — unlink only; the subscription stays active
curl -X DELETE "$C/subscriptions/subscription-acme-analytics" -H "X-API-KEY: <KEY>"
Idempotent: re-attaching, or detaching what isn't attached, changes nothing. A subscription on a different contract is rejected, not moved.
Don't use
PATCH /contracts/:idto add a subscription. ItssubscriptionIdsreplaces the whole set, unlinking any ID you leave out. Use it only to declare complete membership.
Amend, Archive, Read
PATCH /contracts/:id (name, PO number, activation dates), GET /contracts/:id,
GET /customers/:customerId/contracts, GET /contracts?state=ACTIVE, POST /contracts/:id/archive.
Archive is not detach. Archiving cancels the contract and every subscription on it — the customer loses access. To remove a subscription while keeping it running, detach it.
What Provisioning Alone Does NOT Give You
No billing contract: billingId and nextInvoice stay null, no invoices are generated, and
cancel-billing rejects the contract — intended, not a misconfiguration. Adding billing later is one-way
(references/billing-and-invoices.md).
Contract State
Stored state is DRAFT until canceled; the state you read back is derived — ACTIVE once any
subscription on it is active. So a provisioning-only contract flips to ACTIVE on its own, with no publish
step. Field table, state machine, error codes, contract.* events: references/contract-model.md.
Set Up Billing
Negotiated terms, priced line items, invoices. Needs a billing contract — created with setupBilling: true, or enabled later on a provisioning-only one (one-way; billing is never removed).
Full walkthrough: references/billing-and-invoices.md. Five things first:
- Confirm the whole reading before the first write. An order form is not a spec. Read back every line
item with its pricing model and rows, the terms, the legal entity, the customer — and get
agreement before any write call — the pre-publish review is too late, the lines already exist.
Surface contradictions rather than resolving them. Order forms lose lines predictably — usage-based lines show no amount,
Includedlines belong at price 0, a per-period table is one contract, "purchase order reference" is oftenN/Aand is not the order number. The reference covers each. - Line items are priced by existing pricing models — never a table you build.
GET /contracts/pricing-modelslists them withisPayAsYouGoandeditableInputs; supply only the inputs a model exposes, named asrowsaccepts them. Charge type is in advance (IN_ADVANCE) or in arrears (IN_ARREARS) — the engine'sDURING_USE/AFTER_USEare rejected. Pay-as-you-go applies only to metered items, takes no quantity (usage supplies it), and is always in arrears. Resolve a model byisPayAsYouGo+ shape, never by name — five of the ten share a name with their opposite-category twin, and both accept the same rows and return 200.references/pricing-models.mdmaps deal shapes to models, with the nearest wrong neighbour for each. - A legal entity needs a payment account.
GET /contracts/legal-entitieslists each entity's accounts; an emptypaymentAccountsis the common half-configured state and no API creates one — raise it before building, or the contract looks complete then refuses to publish. Set both, plus period, currency and payment terms, withPATCH .../billing/settingsand.../billing/terms— contract-wide, so apply them before any per-item exception. - Two checks before publishing, both real on a draft.
GET .../billing/summaryagainst the order form's figures, andGET .../billing/invoice-schedule(?count=N) against its billing schedule. The totals can be exactly right while the invoices are wrong — items agreeing on money but differing on when they bill split into extra invoices, and only the schedule shows it. Publishing refuses without a period, payment terms and a legal entity. - A live contract is amended, not rewritten.
POST .../billing/edit-activechanges it in place — it stays active and only affected invoices regenerate. Needs a body —{}if nothing to choose. The line-item and terms endpoints instead reset a live contract to a draft, dropping unsent invoices. Rejections areBillingContractOperationRejected, with the remedy in the message; read it rather than retrying.BillingContractEditBlockedis terminal: credit note or new contract.
Invoices and credit notes: same reference.
When NOT to Use This Skill
- Self-serve catalog modeling — plans, addons, charges, coupons →
stigg-pricing-modeling. - Subscription lifecycle with no contract attached →
stigg-subscriptions. - Credit currencies, grants, ledger →
stigg-credits. - Per-entity budgets and usage limits →
stigg-governance.
Common Mistakes
Each mistake — the fix:
- Omitting
setupBillingfor provisioning only — defaults totrue; sendfalse. - Attaching a self-serve subscription — self-serve plans can't join. Use a custom plan.
PATCH subscriptionIdsto add one — it replaces the whole set. UsePOST /contracts/:id/subscriptions.- Archiving to remove a subscription — it cancels the contract and every subscription. Detach instead.
- Treating the contract as what grants entitlements — the subscriptions do. An empty contract grants nothing and holds no price or quantity.
- Retrying a 409 on product duplication — a modeling conflict, not transient. Detach that product's subscription first.
- Inventing a pricing table — pricing comes from an existing model, chosen by id. Fill only the inputs it exposes.
- Publishing without reading the summary — afterwards those figures are what the customer is invoiced.
- Retrying
BillingContractEditBlocked— terminal: a credit note or a new contract. - Expecting a URL for an invoice PDF — inline base64, no durable link.
GET .../pdfwaits, or poll the generate/status pair.