1. Overview
EasyBilling is the subscription billing platform used by this system. It is responsible for:- Managing customer accounts and subscription contracts
- Supporting usage-based billing
- Processing actual payments through Stripe Connect
- Providing invoice generation and a customer self-service portal
Authorization: Bearer <EASYBILLING_API_TOKEN>trace-id: <UUID>
2. Environment Variable Configuration
Required:| Variable | Description |
|---|---|
EASYBILLING_BASE_URL | API base URL (sandbox: https://sbx.api.easybilling.cloud/billing) |
EASYBILLING_API_TOKEN | API key obtained from EasyBilling |
EASYBILLING_DEFAULT_PLAN_ID | Default (free/starter) plan UUID |
EASYBILLING_UPGRADE_PLAN_ID | Paid upgrade plan UUID |
EASYBILLING_USAGE_SCHEMA_NAME | Usage event schema name. Must be defined in EasyBilling first |
EASYBILLING_PAYMENT_SUCCESS_URL | Redirect URL after Stripe payment success |
EASYBILLING_PAYMENT_CANCEL_URL | Redirect URL after Stripe payment cancellation |
EASYBILLING_CUSTOMER_PORTAL_URL | Customer portal base URL (sandbox: https://sbx.app.easybilling.cloud/customer/portal). EasyBilling also supports custom domains, e.g. https://billing.yourcompany.com/customer/portal |
EASYBILLING_INVOICE_TEMPLATE_ID | Invoice PDF template UUID |
3. Account Creation
When a user registers, create the corresponding account in EasyBilling. Endpoint:POST /api/accounts
Request body:
- The account number field name is
number, notaccountNumber. - The account number should use your own system’s customer account number.
- If you do not pass an account number, EasyBilling will generate its own.
- If you do not use your own customer account number, you must store EasyBilling
numberin your user table. All follow-up EasyBilling operations depend on this field.
4. Contract Creation
After registration (or on demand), create a contract for the default plan. Endpoint:POST /api/contract-actions
Request body:
effectiveDatemust be today inYYYY-MM-DDformat. Do not use a fixed date such as the first day of a month.- Set
expirationDateas needed. For a one-year contract, set one year later (for example,2027-04-13). expirationDatemeans the contract expires at 00:00 on that date, so that date is not included.createContractWithPlanmust be an array.
contractInfo.id to your user table as easyBillingContractId. This field is required for both upgrade and cancellation.
5. Plan Upgrade
Switch a user from the current plan to a paid plan. Endpoint:POST /api/contract-actions
Request body:
paymentResults[].sessionUrl and return it to the frontend.
Recovery path (when contractId is empty): If contract creation failed during registration and contractId is empty, first create a contract directly with the target upgrade plan (skip switch-plan). Then extract sessionUrl from the response.
6. Contract Cancellation
Cancel the active contract immediately. Endpoint:POST /api/contract-actions
Request body:
- Set
effectiveDateto tomorrow (today + 1 day). Cancellation takes effect on the next day. - If today’s usage has already been uploaded, the earliest possible cancellation is tomorrow.
- EasyBilling automatically handles refunds and tax reversal. No separate backend calculation is required.
- After cancellation succeeds, update user
subscriptionStatustoCANCELEDin your own system.
7. Usage Event Reporting
Whenever a user completes a billable action (for example, one AI analysis), report usage to EasyBilling. Endpoint:POST /api/usage-events
Important: The request body must be an array.
eventTimemust be ISO 8601 UTC format.- Use UUID v4 for
eventIdto ensure idempotency. - Asynchronous reporting is recommended. On failure, log the error in your business table and do not block the main flow.
attributesmust match the usage event schema definition. The sample above is only an example.
8. Invoice List Query
Endpoint:GET /api/invoices?accountNumber=SM-ACC-00000001
Response field mapping (important):
| EasyBilling Field | Meaning | Suggested Mapping |
|---|---|---|
number | Invoice number (e.g. INV-000001) | invoiceNumber |
id | Invoice UUID | invoiceId |
date | Invoice date | invoiceDate |
totalAmount | Total amount (numeric) | amount |
dueDate | Due date | dueDate |
currency | Currency | currency |
9. Invoice PDF Generation
Endpoint:POST /template-engine/api/pdf/generate
Request body:
nameshould use the invoicenumberfield, i.e.invoiceNumber.objectIdshould use the invoiceidfield, i.e.invoiceId.- Before calling PDF API, verify the invoice belongs to the current user account by checking the invoice list.
- The response
idispdfId. Download PDF viaGET /template-engine/api/pdf/{pdfId}.
10. Customer Portal
The customer portal allows users to self-manage subscriptions and view invoices.Step 1: Get a Short-lived Token
Endpoint:POST /api/authenticate/account-auth
Step 2: Build the Portal URL
11. Common Notes
| # | Issue | Correct Approach |
|---|---|---|
| 1 | effectiveDate set to start of month (e.g. 2026-04-01) | Always use today’s date |
| 2 | Reading accountNumber from account creation response | Response field is number |
| 3 | Treating account creation and contract creation as one step | If account creation succeeds but contract creation fails, retry contract creation separately |
| 4 | Forgetting array wrapper for usage event body | POST /api/usage-events body must be [event] |
| 5 | Not handling token field variants | Check all 6 candidate locations in account-auth response |
| 6 | Generating PDF without ownership verification | Confirm (name, objectId) exists in current user’s invoice list first |
| 7 | Calling switch-plan when contractId is empty | Use recovery path: create contract first, then upgrade |
Quick Checklist
For new project integration, verify:- Environment variables are configured (8 required items)
- Account creation response uses
number, notaccountNumber - Contract creation uses today’s
effectiveDate contractInfo.idis persisted in user table- Usage event body is an array
account-authtoken extraction supports all 6 response formats- Portal URL containing token is returned only from backend
