SaaS Invoice Billing
Bill subscriptions with crypto invoices, expiry timers, and partial payments.
Open the interactive version or read this guide as markdown.
Tags: Invoices, Webhooks
Paste this prompt into Claude, ChatGPT, or any AI tool. It works best with the CoinCircuit MCP server connected: https://mcp.coincircuit.io
You are helping me integrate CoinCircuit crypto invoicing into my existing SaaS billing system. I have the CoinCircuit MCP server connected.
If you have access to the CoinCircuit MCP server, call these tools for the most accurate and detailed schema outputs:
- Call
get_endpointwith methodpostand path/api/v1/invoicesin the CoinCircuit MCP for the full invoice creation schema. - Call
search_apiwith queryinvoice webhookand typeschemasto list all invoice webhook payload schemas. - Call
get_schemawith nameInvoicePaidWebhookDtofor the exact invoice.paid webhook payload. - Call
get_schemawith nameInvoiceExpiredWebhookDtofor the invoice.expired webhook payload.
Use the live MCP data as your source of truth. The details below are a guide, but if the MCP returns something different, trust the MCP.
API Basics
- Base URL:
https://api.coincircuit.io - Auth:
x-api-keyheader on every request.
Project Context
I have a SaaS app that bills customers monthly. I'm adding crypto invoice payments via CoinCircuit so customers can pay in any supported crypto (USDT, BTC, ETH, etc.).
What I Need You to Implement
1. Invoice creation on subscription renewal
Endpoint: POST /api/v1/invoices
(Call get_endpoint with method post, path /api/v1/invoices, section request in the CoinCircuit MCP for the exact request body schema with all field types and validations.)
Required fields:
amount(string) - the subscription price as a string with up to 2 decimal places, e.g."29.99"currency(string) -"NGN"or"USD"description(string) - e.g. "Pro Plan - May 2026"expiresAt(string, ISO 8601) - when the invoice expires. Max 2 days from creation. Defaults to 12 hours if omitted.customer(object) - must includeemail(required). Optional:firstName,lastName,phone(E.164),telegramId
Optional fields:
reference(string) - custom invoice reference, e.g."INV-2026-005". Auto-generated if omitted.periodStart/periodEnd(string, ISO 8601) - billing period dates for subscription contextnextPaymentDate(string, ISO 8601) - next billing date, shown on the invoicemetadata(object) - storesubscriptionId,plan, etc. for webhook reconciliationsuccessUrl/cancelUrl(string) - redirect URLs after paymentasset(string) - lock to a specific crypto ("BTC","ETH","USDT","USDC","SOL","BNB","TRX")chain(string) - lock to a specific blockchain ("bitcoin","ethereum","solana","bsc","tron","base","arbitrum")
Example request body:
{
"amount": "29.99",
"currency": "USD",
"description": "Pro Plan - May 2026",
"expiresAt": "2026-05-03T00:00:00.000Z",
"customer": {
"email": "subscriber@example.com",
"firstName": "John",
"lastName": "Doe"
},
"periodStart": "2026-05-01T00:00:00.000Z",
"periodEnd": "2026-05-31T23:59:59.000Z",
"nextPaymentDate": "2026-06-01T00:00:00.000Z",
"metadata": {
"subscriptionId": "sub_abc123",
"plan": "pro"
},
"successUrl": "https://myapp.com/billing/success"
}
Response (201): Returns the invoice object. Key fields:
data.id- invoice UUIDdata.reference- invoice reference (e.g."inv_ref_abc123xyz789")data.url- payment URL (e.g.https://checkout.coincircuit.io/invoice/inv-abc123)data.status- starts as"pending", transitions to"partial","paid", or"expired"data.state-"open"(accepting payments) or"closed"(finalized)data.amount/data.amountPaid- track payment progressdata.isRefunded- boolean
(Call get_endpoint with method post, path /api/v1/invoices, section success in the CoinCircuit MCP for the full response schema.)
Error responses: 400 (invalid input or expiresAt exceeds 2 days), 401 (bad API key)
2. Send the payment link
The response includes data.url. Email this to the customer or display it in your app's billing dashboard. The hosted page handles crypto selection, address display, and payment detection automatically.
3. Webhook event handling
(Call get_schema with name InvoicePaidWebhookDto in the CoinCircuit MCP for the full invoice.paid webhook payload with all nested fields including the payments array.)
Invoice events (envelope: { event: string, data: { invoice: InvoiceObject, payments?: PaymentArray } }):
invoice.created- confirmation the invoice was created. Good for logging. (Callget_schemawith nameInvoiceCreatedWebhookDtofor details.)invoice.paid- full payment received. Activate or extend the subscription. Thedata.paymentsarray contains details on each payment session:asset,chain,amount,address,txHash,explorerUrl,status.invoice.updated- partial payment received. Checkdata.invoice.amountPaidvsdata.invoice.amountto track progress.data.invoice.statuswill be"partial". (Callget_schemawith nameInvoiceUpdatedWebhookDtofor details.)invoice.expired- invoice expired unpaid. Suspend access or create a new invoice with a reminder email. (Callget_schemawith nameInvoiceExpiredWebhookDtofor details.)
Invoice webhook data fields:
data.invoice.reference- matches your invoicedata.invoice.metadata- yoursubscriptionIdandplanare heredata.invoice.amount/data.invoice.amountPaid- track cumulative paymentsdata.invoice.status-"pending","partial","paid","expired"data.invoice.paidAt- ISO 8601 timestamp when fully paiddata.invoice.periodStart/data.invoice.periodEnd- billing perioddata.invoice.customer.email- for confirmation emails
Webhook security: Same as checkout: verify HMAC-SHA256 via x-coincircuit-signature, deduplicate via x-coincircuit-delivery-id.
Constraints
- Invoice
amountis a string with up to 2 decimal places (e.g."29.99"), same as checkout sessions expiresAtmax is 2 days from creation. Defaults to 12 hours.- Invoices support partial payments. Track
amountPaidbefore activating the subscription. - The invoice stays
"open"until fully paid or expired - Supported currencies: NGN, USD