mcpField
S
stigg-contracts

stigg-contracts

github.com/stiggioMarketing & growth246 installsUpdated 4w ago
Repository

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

  1. 01Create contracts and manage subscription links
  2. 02Configure billing terms and priced items
  3. 03Check billing totals and invoice schedules
  4. 04Handle invoices and credit notes
  5. 05Explain contract lifecycle constraints

Use cases

  1. 01Set up subscription entitlements for a sales contract
  2. 02Configure billing and invoices from order terms
  3. 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-contracts

After installing, type/stigg-contractsin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
billing-and-invoices.md
contract-model.md
pricing-models.md

Details

From the project README

Stigg 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-sdk or @stigg/js-client-sdk. Use REST (canonical here, https://api.stigg.io/api/v1, header X-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.

ChoiceWhat the contract doesSend
Provisioning onlyGroups custom subscriptions and grants their entitlements. No invoices.setupBilling: false + subscriptions
Billing onlyPriced line items and invoices, no subscriptions attached.setupBilling: true, no subscriptions
BothEntitlements 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 — setupBilling included. 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

OpPurposeReference
Create, attach, detach, amend, archive, readThe contract and its membershipinline below
Set up billingTerms, settings, priced line items, totals, publishbelow + references/billing-and-invoices.md
Pick a pricing modelDeal shape → model + rows, and the shapes with no modelreferences/pricing-models.md
InvoicesList, filter, one invoice, the PDF, mark paidreferences/billing-and-invoices.md
Credit notesSettle a change once a contract can no longer be editedreferences/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/:id to add a subscription. Its subscriptionIds replaces 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:

  1. 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, Included lines belong at price 0, a per-period table is one contract, "purchase order reference" is often N/A and is not the order number. The reference covers each.
  2. Line items are priced by existing pricing models — never a table you build. GET /contracts/pricing-models lists them with isPayAsYouGo and editableInputs; supply only the inputs a model exposes, named as rows accepts them. Charge type is in advance (IN_ADVANCE) or in arrears (IN_ARREARS) — the engine's DURING_USE/AFTER_USE are rejected. Pay-as-you-go applies only to metered items, takes no quantity (usage supplies it), and is always in arrears. Resolve a model by isPayAsYouGo + 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.md maps deal shapes to models, with the nearest wrong neighbour for each.
  3. A legal entity needs a payment account. GET /contracts/legal-entities lists each entity's accounts; an empty paymentAccounts is 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, with PATCH .../billing/settings and .../billing/terms — contract-wide, so apply them before any per-item exception.
  4. Two checks before publishing, both real on a draft. GET .../billing/summary against the order form's figures, and GET .../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.
  5. A live contract is amended, not rewritten. POST .../billing/edit-active changes 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 are BillingContractOperationRejected, with the remedy in the message; read it rather than retrying. BillingContractEditBlocked is 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 setupBilling for provisioning only — defaults to true; send false.
  • Attaching a self-serve subscription — self-serve plans can't join. Use a custom plan.
  • PATCH subscriptionIds to add one — it replaces the whole set. Use POST /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 .../pdf waits, or poll the generate/status pair.

Comments

Markdown supported