Enterprise Contract Onboarding
A complete walkthrough of an enterprise deal: ramp pricing, true-up billing, and contract amendment when the deal expands. All examples use curl.
Scenario: Acme Corp signs a $325k annual contract with ramp pricing:
- Q1: $50k (land and expand)
- Q2: $75k (ramping up)
- Q3-Q4: $100k/quarter (full rate)
Step 1: Create the Customer and Contract
# Create the enterprise customer with legal name for invoice header
CUSTOMER=$(curl -s -X POST https://api.bill.sh/admin/v1/customers \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Acme Corp",
"legal_name": "Acme Corporation, Inc.",
"email": "billing@acmecorp.com",
"currency": "USD",
"account_type": "Organization",
"payment_terms_days": 30,
"tax_id": "US-EIN-12-3456789"
}')
CUSTOMER_ID=$(echo $CUSTOMER | jq -r '.id')
echo "Customer: $CUSTOMER_ID"
# Create the contract with ramp terms
CONTRACT=$(curl -s -X POST https://api.bill.sh/admin/v1/contracts \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"customer_id\": \"$CUSTOMER_ID\",
\"name\": \"Acme Corp — Enterprise Platform 2026\",
\"start_date\": \"2026-01-01\",
\"end_date\": \"2026-12-31\",
\"terms\": [
{
\"phase\": 1,
\"start_date\": \"2026-01-01\",
\"end_date\": \"2026-03-31\",
\"committed_amount_nanos\": \"50000000000000000\",
\"description\": \"Q1 — Ramp: \$50k\"
},
{
\"phase\": 2,
\"start_date\": \"2026-04-01\",
\"end_date\": \"2026-06-30\",
\"committed_amount_nanos\": \"75000000000000000\",
\"description\": \"Q2 — Ramp: \$75k\"
},
{
\"phase\": 3,
\"start_date\": \"2026-07-01\",
\"end_date\": \"2026-12-31\",
\"committed_amount_nanos\": \"200000000000000000\",
\"description\": \"Q3-Q4 — Full rate: \$100k/quarter\"
}
]
}")
CONTRACT_ID=$(echo $CONTRACT | jq -r '.id')
echo "Contract: $CONTRACT_ID (status: $(echo $CONTRACT | jq -r '.status'))"
Step 2: Link a Subscription to the Contract
Enterprise customers typically get a custom plan with rate overrides. Here we subscribe them to the Enterprise plan and link it to the contract:
# Create the subscription linked to the contract
SUB=$(curl -s -X POST https://api.bill.sh/v1/subscriptions \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sub-acme-enterprise-2026" \
-d "{
\"customer_id\": \"$CUSTOMER_ID\",
\"plan_id\": \"$ENTERPRISE_PLAN_ID\",
\"contract_id\": \"$CONTRACT_ID\",
\"currency\": \"USD\",
\"start_date\": \"2026-01-01\"
}")
SUB_ID=$(echo $SUB | jq -r '.id')
echo "Subscription: $SUB_ID ($(echo $SUB | jq -r '.status'))"
Step 3: Monthly Billing with Commit Draw-Down
Each month, trigger billing. The platform draws down from the quarterly commit before charging the payment method:
# End of January — trigger billing
curl -s -X POST "https://api.bill.sh/admin/v1/subscriptions/$SUB_ID/bill" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Idempotency-Key: "bill-acme-2026-01"
# List the resulting Draft invoice
INVOICES=$(curl -s "https://api.bill.sh/admin/v1/invoices?customer_id=$CUSTOMER_ID&status=Draft" \
-H "Authorization: Bearer $ADMIN_TOKEN")
INV_ID=$(echo $INVOICES | jq -r '.[0].id')
echo "Draft invoice: $INV_ID"
# Finalize the invoice
INV=$(curl -s -X POST "https://api.bill.sh/admin/v1/invoices/$INV_ID/finalize" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Idempotency-Key: finalize-$INV_ID")
echo "Invoice: $(echo $INV | jq -r '.invoice_number') — \$$(echo $INV | jq -r '.total_nanos | tonumber / 1e12 | tostring')"
Step 4: True-Up at Quarter End
At Q1 end, if the customer used less than their $50k commit, a true-up charge covers the difference:
# Request a true-up calculation for Q1
TRUE_UP=$(curl -s -X POST \
"https://api.bill.sh/admin/v1/contracts/$CONTRACT_ID/true-up" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"period_start": "2026-01-01",
"period_end": "2026-03-31"
}')
SHORTFALL=$(echo $TRUE_UP | jq -r '.shortfall_nanos')
if [ "$SHORTFALL" != "0" ] && [ "$SHORTFALL" != "null" ]; then
SHORTFALL_USD=$(echo "scale=2; $SHORTFALL / 1000000000000" | bc)
echo "True-up required: \$$SHORTFALL_USD"
# The true-up creates an invoice line item automatically
TRUE_UP_INV_ID=$(echo $TRUE_UP | jq -r '.invoice_id')
echo "True-up invoice: $TRUE_UP_INV_ID"
else
echo "No true-up required — customer met commit"
fi
Step 5: Contract Amendment When Deal Expands
In June, Acme adds a Data Analytics module, expanding the contract to $450k total. Amendment is immutable — it creates a new contract linked to the old one:
# Amend the contract
AMENDED=$(curl -s -X POST \
"https://api.bill.sh/admin/v1/contracts/$CONTRACT_ID/amend" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reason": "Expansion — Data Analytics module added per SOW-2026-06",
"effective_date": "2026-07-01",
"additional_terms": [
{
"phase": 4,
"start_date": "2026-07-01",
"end_date": "2026-12-31",
"committed_amount_nanos": "250000000000000000",
"description": "Q3-Q4 expanded — \$125k/quarter (was \$100k)"
}
]
}')
NEW_CONTRACT_ID=$(echo $AMENDED | jq -r '.id')
PARENT_CONTRACT_ID=$(echo $AMENDED | jq -r '.parent_contract_id')
echo "New contract: $NEW_CONTRACT_ID"
echo "Amendment chain: $PARENT_CONTRACT_ID → $NEW_CONTRACT_ID"
Key Takeaways
- Contracts track commercial terms; subscriptions drive billing — they’re separate concerns
- Ramp pricing is modeled as multiple
ContractTermphases with differentcommitted_amount_nanos - True-up runs at phase boundaries to collect any shortfall
- Amendment is immutable: the
parent_contract_idfield traces the full history - Rate overrides (
RateOverride) can apply custom pricing at the SKU level without touching the catalog
See Contracts for the full API reference.