> ## Documentation Index
> Fetch the complete documentation index at: https://docs.easybilling.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# EasyBilling Integration Guide

This document is for new merchants. It provides a structured overview of all key points required to integrate with the EasyBilling billing platform, including environment setup, API workflows, field mapping notes, and common pitfalls.

## 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

**Sandbox Base URL:**

```text theme={null}
https://sbx.api.easybilling.cloud/billing
```

**Production Base URL:**

```text theme={null}
https://app.api.easybilling.cloud/billing
```

All requests must include these headers:

* `Authorization: Bearer <EASYBILLING_API_KEY>`
* `trace-id: <UUID>`

## 2. API Key

There are two ways to create an API Key in EasyBilling:

### Option 1: From System Configuration

1. Navigate to **System Configuration** → **Users & API Keys**.
2. Locate your user and click the **Edit** (pencil) icon.
3. Click **New API Key**.
4. Enter a name for the API Key.
5. Save to generate the API Key.

### Option 2: From Your Profile

1. Click your user avatar in the bottom-left corner.
2. Select **Profile**.
3. Click **New API Key**.
4. Enter a name for the API Key .
5. Save to generate the API Key.

### Copying Your API Key

After the API Key is created:

1. Locate the newly generated API Key in the list.
2. Click the **Copy** button next to the API Key.
3. Store the API Key securely, as it will be used to authenticate API requests.

## 3. Environment Variable Configuration

Required:

| Variable                          | Description                                                                                                                                                                               |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `EASYBILLING_BASE_URL`            | API base URL (sandbox: `https://sbx.api.easybilling.cloud/billing`)                                                                                                                       |
| `EASYBILLING_API_KEY`             | API key obtained from EasyBilling in step 2                                                                                                                                               |
| `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                                                                                                                                                                 |

## 4. Account Creation

When a user registers, create the corresponding account in EasyBilling.

**Endpoint:** `POST /api/accounts`

**Request body:**

```json theme={null}
{
  "name": "User Name",
  "number": "SM-ACC-00000001",
  "email": "user@example.com",
  "addressLine1": "123 Main St",
  "addressLine2": "",
  "country": "US",
  "state": "CA",
  "city": "San Francisco",
  "postalCode": "94102"
}
```

**Key notes:**

* The account number field name is `number`, **not** `accountNumber`.
* 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 `number` in your user table. All follow-up EasyBilling operations depend on this field.
* Always query the account by accountNumber before creating one. If the account already exists, reuse its accountNumber and proceed to create the contract. Only create a new account when no existing account is found. This prevents duplicate accounts and avoids integration errors when a previous request successfully created the account but failed to create the contract.

## 5. Contract Creation

After registration (or on demand), create a contract for the default plan.

**Endpoint:** `POST /api/contract-actions`

**Request body:**

```json theme={null}
{
  "accountNumber": "SM-ACC-00000001",
  "actions": [
    {
      "type": "create-contract-with-plan",
      "createContractWithPlan": [
        {
          "planId": "<EASYBILLING_DEFAULT_PLAN_ID>",
          "effectiveDate": "2026-04-13",
          "currency": ["USD"]
        }
      ],
      "immediatelyPay": true,
      "paymentInfo": {
        "paymentGatewayType": "stripe-connect",
        "paymentSuccessUrl": "<EASYBILLING_PAYMENT_SUCCESS_URL>",
        "paymentCancelUrl": "<EASYBILLING_PAYMENT_CANCEL_URL>"
      }
    }
  ]
}
```

**Key notes:**

* `effectiveDate` must be **today** in `YYYY-MM-DD` format. Do not use a fixed date such as the first day of a month.
* Set `expirationDate` as needed. For a one-year contract, set one year later (for example, `2027-04-13`).
* `expirationDate` means the contract expires at 00:00 on that date, so that date is not included.
* `createContractWithPlan` must be an array.

**Response (must be persisted):**

```json theme={null}
{
  "contractActionNumber": "CA-000001",
  "contractInfo": {
    "id": "contract-uuid",
    "contractNumber": "CT-000001",
    "status": "active"
  },
  "paymentResults": []
}
```

Persist `contractInfo.id` to your user table as `easyBillingContractId`. This field is required for both upgrade and cancellation.

## 6. Plan Upgrade

Switch a user from the current plan to a paid plan.

**Endpoint:** `POST /api/contract-actions`

**Request body:**

```json theme={null}
{
  "accountNumber": "SM-ACC-00000001",
  "actions": [
    {
      "type": "switch-plan",
      "switchPlan": {
        "contractId": "<easyBillingContractId>",
        "planId": "<EASYBILLING_UPGRADE_PLAN_ID>",
        "effectiveDate": "2026-04-13",
        "currency": ["USD"]
      },
      "immediatelyPay": true,
      "paymentInfo": {
        "paymentGatewayType": "stripe-connect",
        "paymentSuccessUrl": "<EASYBILLING_PAYMENT_SUCCESS_URL>",
        "paymentCancelUrl": "<EASYBILLING_PAYMENT_CANCEL_URL>"
      }
    }
  ]
}
```

**Response handling:**

```json theme={null}
{
  "contractActionNumber": "CA-000002",
  "paymentResults": [
    {
      "sessionUrl": "https://checkout.stripe.com/..."
    }
  ]
}
```

Extract the Stripe Checkout URL from `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.

## 7. Contract Cancellation

Cancel the active contract immediately.

**Endpoint:** `POST /api/contract-actions`

**Request body:**

```json theme={null}
{
  "accountNumber": "SM-ACC-00000001",
  "actions": [
    {
      "type": "cancel",
      "cancel": {
        "contractId": "<easyBillingContractId>",
        "effectiveDate": "{{today()+1}}"
      }
    }
  ]
}
```

**Notes:**

* Set `effectiveDate` to **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 `subscriptionStatus` to `CANCELED` in your own system.

## 8. 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.

```json theme={null}
[
  {
    "eventId": "uuid-v4",
    "schemaName": "speech-master",
    "eventTime": "2026-04-13T08:00:00.000Z",
    "accountNumber": "SM-ACC-00000001",
    "attributes": [
      { "name": "quantity", "value": "1" }
    ]
  }
]
```

**Key notes:**

* `eventTime` must be ISO 8601 UTC format.
* Use UUID v4 for `eventId` to ensure idempotency.
* Asynchronous reporting is recommended. On failure, log the error in your business table and do not block the main flow.
* `attributes` must match the usage event schema definition. The sample above is only an example.

## 9. Invoice List Query

**Endpoint:** `POST /api/data-query`

**Request body:**

```json theme={null}
{
    "queryObject": "invoice",
    "filters": [
        {
            "fieldName": "accountNumber",
            "fieldValues": [
                "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`        |

## 10. Invoice PDF Generation

**Endpoint:** `POST /template-engine/api/pdf/generate`

**Request body:**

```json theme={null}
{
  "name": "INV-000000325",
  "objectId": "9efc7d38-0d97-4cc3-a861-fdf4613d9d75",
  "objectType": "invoice",
  "templateId": "<EASYBILLING_INVOICE_TEMPLATE_ID>"
}
```

**Notes:**

* `name` should use the invoice `number` field, i.e. `invoiceNumber`.
* `objectId` should use the invoice `id` field, i.e. `invoiceId`.
* Before calling PDF API, verify the invoice belongs to the current user account by checking the invoice list.
* The response `id` is `pdfId`. Download PDF via `GET /template-engine/api/pdf/{pdfId}`.

## 11. 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`

```json theme={null}
{
  "accountNumber": "SM-ACC-00000001"
}
```

**Token field compatibility (check in priority order):**

```text theme={null}
response.token
response.accessToken
response.accountToken
response.data.token
response.data.accessToken
response.data.accountToken
```

Only log the first 6 characters of the token. Never output the full token.

### Step 2: Build the Portal URL

```text theme={null}
https://sbx.app.easybilling.cloud/customer/portal?token=<TOKEN>
```

Use URL API to build safely and avoid encoding issues from manual string concatenation:

```typescript theme={null}
const portalUrl = new URL(env.easyBillingCustomerPortalUrl); // EASYBILLING_CUSTOMER_PORTAL_URL
portalUrl.searchParams.set('token', token);
return portalUrl.toString();
```

**Important:** The portal URL contains sensitive token data. It must be returned by a backend endpoint and must not be hardcoded or persisted on frontend.

## 12. Notification Configuration

EasyBilling supports webhook notifications for the following events:

| Event               | Description                                           |
| :------------------ | :---------------------------------------------------- |
| **Prepaid Credits** | Sent when your prepaid credits balance reaches **0**. |
| **Payment Success** | Sent when a payment is successfully processed.        |

### Configure a Webhook Endpoint

1. Navigate to **System Configuration** → **Notifications**.
2. Locate the notification event you want to configure.
3. Click the **Edit** (pencil) icon.
4. Enter your webhook HTTP URL.
5. Click **Save**.

After the webhook URL has been saved, you can view and copy the webhook secret to verify that incoming webhook requests were sent by EasyBilling.

### Webhook Delivery & Retries

EasyBilling will attempt to deliver each webhook event to your configured endpoint.

If a delivery attempt fails (for example, due to a network error or a non-success HTTP response), EasyBilling will automatically retry the notification:

* **Retry Attempts:** 2 additional retries
* **Retry Interval:** 1 minute between each retry

## 13. 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`, not `accountNumber`
* Contract creation uses today's `effectiveDate`
* `contractInfo.id` is persisted in user table
* Usage event body is an array
* `account-auth` token extraction supports all 6 response formats
* Portal URL containing token is returned only from backend
