curl --request POST \
--url https://api.saasybill.com/v1/subscriptions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"customer_code": "ACME-001",
"plan_code": "TEAM-MONTHLY",
"units": 10,
"start_date": "2026-09-30",
"discount_percent": "10",
"label": "Acme � Team plan",
"invoice_reference": "PO-4471"
}
'{
"id": "9d4e2b7a-13c5-4f8e-b6a0-2e1d7c9f5b33",
"object": "subscription",
"customer": "3b9f1c1e-6a52-4c0e-9d7e-1f6f2f0b7a11",
"customer_code": "ACME-001",
"plan": "b6c1e0a4-2f7d-4a61-8f0c-5d3e9a7b2c44",
"status": "active",
"currency": "AUD",
"interval": {
"unit": "month",
"count": 1
},
"timezone": "Australia/Sydney",
"start_date": "2026-09-30",
"renewal_date": "2026-10-30",
"end_date": null,
"units": 10,
"minimum_units": 5,
"discount_percent": "10.00",
"cancel_at_period_end": false,
"end_after_cycles": null,
"label": "Acme � Team plan",
"invoice_reference": "PO-4471",
"renewal_value": "108.0000",
"usage_value": "0.0000",
"usage_value_annual": "0.0000",
"minimum_usage_value": "0.0000",
"meters": [],
"cpi_enabled": false,
"created": "2026-09-30T04:12:00.000Z",
"invoice": {
"id": "5f8a3c9d-7e21-4b04-9c6d-0a2b4e8f1d55",
"object": "invoice",
"customer": "3b9f1c1e-6a52-4c0e-9d7e-1f6f2f0b7a11",
"subscription": "9d4e2b7a-13c5-4f8e-b6a0-2e1d7c9f5b33",
"number": "INV-0042",
"reference": "PO-4471",
"status": "awaiting_approval",
"currency": "AUD",
"invoice_date": "2026-09-29T14:00:00.000Z",
"due_date": "2026-10-29T13:00:00.000Z",
"subtotal": "558.0000",
"total": "613.8000",
"credit_note_amount": "0.0000",
"amount_paid": null,
"sent": false,
"xero": {
"sync_status": "synced",
"invoice_id": "f0e9d8c7-b6a5-4f43-9210-fedcba987654",
"sync_error": null
},
"lines": [
{
"id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c77",
"type": "subscription",
"revenue_type": "new_subscription",
"meter": null,
"usage_quantity": null,
"name": "Team",
"description": "Team � 10 seats",
"units": 10,
"amount": "120.0000",
"discount_amount": "12.0000",
"charge_amount": "108.0000",
"effective_date": "2026-09-29T14:00:00.000Z"
},
{
"id": "1b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d88",
"type": "charge",
"revenue_type": "new_subscription",
"meter": null,
"usage_quantity": null,
"name": "Onboarding",
"description": "One-off setup and training",
"units": null,
"amount": "450.0000",
"discount_amount": "0.0000",
"charge_amount": "450.0000",
"effective_date": "2026-09-29T14:00:00.000Z"
}
],
"created": "2026-09-30T04:12:01.000Z"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "Provide a valid API key as `Authorization: Bearer <key>`.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "permission_error",
"code": "insufficient_scope",
"message": "This API key does not have the `subscriptions:write` scope.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "not_found",
"code": "subscription_not_found",
"message": "There is no subscription with that id.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "conflict",
"code": "idempotency_conflict",
"message": "This `Idempotency-Key` was used with a different request. Use a new key for a different request.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "invalid_request_error",
"code": "invalid_parameter",
"message": "`units`: Invalid input: expected number, received string",
"param": "units",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "rate_limit_error",
"code": "rate_limited",
"message": "Too many requests. Try again in 12 seconds.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "api_error",
"code": "internal_error",
"message": "Something went wrong on our side. Retry the request; if it keeps failing, quote the request id.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}Create a subscription
Creates the subscription and its first invoice, and sends the invoice to Xero, under the same rules as the New Subscription form. With a start_date still to come the first invoice can be raised later, as first_invoice says (the organisation’s First Invoice Timing setting decides when it is left out): the response then has invoice: null and a first_invoice_date. Idempotency-Key is required: a repeat of this request would otherwise be a second subscription and a second bill. A retry with the same key and body returns the first response. If Xero refuses the invoice the subscription still exists and the response is 201 with invoice.xero.sync_status of failed. 409 subscription_ended when the organisation’s own Saasybill subscription has ended: it cannot start new billing until it chooses a plan.
curl --request POST \
--url https://api.saasybill.com/v1/subscriptions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"customer_code": "ACME-001",
"plan_code": "TEAM-MONTHLY",
"units": 10,
"start_date": "2026-09-30",
"discount_percent": "10",
"label": "Acme � Team plan",
"invoice_reference": "PO-4471"
}
'{
"id": "9d4e2b7a-13c5-4f8e-b6a0-2e1d7c9f5b33",
"object": "subscription",
"customer": "3b9f1c1e-6a52-4c0e-9d7e-1f6f2f0b7a11",
"customer_code": "ACME-001",
"plan": "b6c1e0a4-2f7d-4a61-8f0c-5d3e9a7b2c44",
"status": "active",
"currency": "AUD",
"interval": {
"unit": "month",
"count": 1
},
"timezone": "Australia/Sydney",
"start_date": "2026-09-30",
"renewal_date": "2026-10-30",
"end_date": null,
"units": 10,
"minimum_units": 5,
"discount_percent": "10.00",
"cancel_at_period_end": false,
"end_after_cycles": null,
"label": "Acme � Team plan",
"invoice_reference": "PO-4471",
"renewal_value": "108.0000",
"usage_value": "0.0000",
"usage_value_annual": "0.0000",
"minimum_usage_value": "0.0000",
"meters": [],
"cpi_enabled": false,
"created": "2026-09-30T04:12:00.000Z",
"invoice": {
"id": "5f8a3c9d-7e21-4b04-9c6d-0a2b4e8f1d55",
"object": "invoice",
"customer": "3b9f1c1e-6a52-4c0e-9d7e-1f6f2f0b7a11",
"subscription": "9d4e2b7a-13c5-4f8e-b6a0-2e1d7c9f5b33",
"number": "INV-0042",
"reference": "PO-4471",
"status": "awaiting_approval",
"currency": "AUD",
"invoice_date": "2026-09-29T14:00:00.000Z",
"due_date": "2026-10-29T13:00:00.000Z",
"subtotal": "558.0000",
"total": "613.8000",
"credit_note_amount": "0.0000",
"amount_paid": null,
"sent": false,
"xero": {
"sync_status": "synced",
"invoice_id": "f0e9d8c7-b6a5-4f43-9210-fedcba987654",
"sync_error": null
},
"lines": [
{
"id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c77",
"type": "subscription",
"revenue_type": "new_subscription",
"meter": null,
"usage_quantity": null,
"name": "Team",
"description": "Team � 10 seats",
"units": 10,
"amount": "120.0000",
"discount_amount": "12.0000",
"charge_amount": "108.0000",
"effective_date": "2026-09-29T14:00:00.000Z"
},
{
"id": "1b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d88",
"type": "charge",
"revenue_type": "new_subscription",
"meter": null,
"usage_quantity": null,
"name": "Onboarding",
"description": "One-off setup and training",
"units": null,
"amount": "450.0000",
"discount_amount": "0.0000",
"charge_amount": "450.0000",
"effective_date": "2026-09-29T14:00:00.000Z"
}
],
"created": "2026-09-30T04:12:01.000Z"
}
}{
"error": {
"type": "authentication_error",
"code": "invalid_api_key",
"message": "Provide a valid API key as `Authorization: Bearer <key>`.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "permission_error",
"code": "insufficient_scope",
"message": "This API key does not have the `subscriptions:write` scope.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "not_found",
"code": "subscription_not_found",
"message": "There is no subscription with that id.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "conflict",
"code": "idempotency_conflict",
"message": "This `Idempotency-Key` was used with a different request. Use a new key for a different request.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "invalid_request_error",
"code": "invalid_parameter",
"message": "`units`: Invalid input: expected number, received string",
"param": "units",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "rate_limit_error",
"code": "rate_limited",
"message": "Too many requests. Try again in 12 seconds.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}{
"error": {
"type": "api_error",
"code": "internal_error",
"message": "Something went wrong on our side. Retry the request; if it keeps failing, quote the request id.",
"request_id": "req_4f1a9c2e7b8d3a605e1c9f02"
}
}subscriptions:write scope. See Authentication.Authorizations
An API key. Its scopes decide what it may call: customers:read (List and look up customers.) customers:write (Create customers, and Xero contacts where the organisation allows it.) catalogue:read (List plans and charges.) subscriptions:read (List and look up subscriptions.) subscriptions:write (Create subscriptions, change units, end and restore them.) invoices:read (List and look up invoices.) invoices:write (Bill a customer once for charges, without a subscription.) credit_notes:read (List and look up credit notes, and where their credit was applied.) usage:write (Send usage events, which are billed to the subscription they name.)
Headers
Body
Raise the plan price, and the rates of any meters the plan bills, by the consumer price index at each renewal. Usage is billed at the rates of the period it was used in. Annual plans only, and only where the organisation's country has a CPI figure or a manual rate. Defaults to false. Cannot be changed through the API after creation.
Response
Success.
The average, over the last three closed usage periods, of what each billed for usage above the meter minimums, net of the discount. An estimate at the plan's current meter prices, and never part of renewal_value. 0 until a usage period has closed, and for a plan that bills no meter.
Was this page helpful?