# Billing

Your credit, and whether it covers your next invoice.

Source: https://docs.pantechdynamics.com/api/billing

Check your credit before creating resources from automation, or poll it so you can top up in time. `standing` tells you at a glance: `covered`, `low` or `empty`. Owners and admins are also emailed once when credit becomes low and once when it runs out.

To add credit, create a purchase and send its `checkout_url` to whoever pays. The link opens Flutterwave for organizations billed in NGN and Stripe for everyone else, so card details never pass through your systems or ours. Nothing is charged until the link is paid.

Endpoints:

- `GET /public/v1/billing/credit`: Get credit
- `POST /public/v1/billing/credit/purchases`: Buy credit
- `GET /public/v1/billing/credit/purchases/{purchase_id}`: Get a credit purchase
- `GET /public/v1/billing/receipts`: List receipts
- `GET /public/v1/billing/receipts/{receipt_id}`: Get a receipt
- `GET /public/v1/billing/receipts/{receipt_id}/pdf`: Download a receipt

## Get credit

```http
GET /public/v1/billing/credit
```

Your organization's credit, and how it compares with your next invoice.

`estimated_next_invoice_minor` is usage so far this billing period plus the recent daily rate for the days left, plus 7.5% VAT on that (VAT applies only to the monthly invoice, never to credit purchases). It is an estimate: the invoice issued at `next_invoice_at` is the real figure. `standing` is `covered` when credit and prepaid instance payments cover the estimate, `low` when they don't, and `empty` when no credit is left. Anything credit doesn't cover is charged to your default payment method.

Check this before creating resources from automation, or poll it to top up in time. Owners and admins are also emailed once when credit becomes `low` and once when it becomes `empty`. To add credit, see *Buy credit*.

Scope: `read`.

Example:

```sh
curl "https://api.pantechdynamics.com/public/v1/billing/credit" \
  -H "Authorization: Bearer $PANTECH_API_KEY"
```

Response `200`:

```json
{
  "currency": "NGN",
  "available_minor": 1,
  "pending_minor": 1,
  "prepaid_minor": 1,
  "estimated_next_invoice_minor": 1,
  "next_invoice_at": "2026-10-03T09:30:00Z",
  "standing": "covered",
  "has_payment_method": true
}
```

Errors:

- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
`BILLING_UNAVAILABLE`: billing is not answering right now. Wait a few seconds, then retry.
## Buy credit

```http
POST /public/v1/billing/credit/purchases
```

Creates a payment link to add credit to your organization. Send `checkout_url` to whoever pays: it opens the payment provider's own secure page, so no card details ever pass through your systems or ours. Organizations pay in NGN or USD. The currency follows your organization's country and can't be chosen.

Nothing is charged until someone pays the link. The credit is added once the provider confirms the payment, usually within a minute: follow the purchase with `GET /public/v1/billing/credit/purchases/{purchase_id}` until `status` is `settled`, and the owners and admins get a receipt by email. A link that isn't paid expires at `checkout_expires_at`; create a new purchase after that.

One purchase is between NGN 500 and NGN 500,000 (`50000` to `50000000` kobo), or between USD 5 and USD 2,000 (`500` to `200000` cents).

Send an `Idempotency-Key`: a retry with the same key returns the same purchase and the same link. The member who created the key must be an owner or admin with a verified email.

Scope: `write`. Send an `Idempotency-Key` header.

Body:

- `amount_minor` (integer, required): How much credit to buy, in minor units of your organization's currency: kobo for NGN, cents for USD.

Example:

```sh
curl -X POST "https://api.pantechdynamics.com/public/v1/billing/credit/purchases" \
  -H "Authorization: Bearer $PANTECH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_minor": 1059200
  }'
```

Response `201`:

```json
{
  "id": "id",
  "status": "pending",
  "amount_minor": 1059200,
  "currency": "NGN",
  "method": "checkout",
  "provider": "provider",
  "checkout_url": "checkout-url",
  "checkout_expires_at": "2026-10-03T09:30:00Z",
  "failure_code": null,
  "settled_at": "2026-10-03T09:30:00Z",
  "created_at": "2026-10-03T09:30:00Z",
  "receipt": {
    "id": "id",
    "number": 100
  }
}
```

Errors:

- `400`: `IDEMPOTENCY_KEY_REQUIRED`: the `Idempotency-Key` header is missing. Send a new unique value, such as a UUID, with every write.
`INVALID_IDEMPOTENCY_KEY`: the `Idempotency-Key` header is longer than 255 characters. Use a shorter value.
`INVALID_REQUEST_BODY`: the body is not a single valid JSON object. Send one JSON object with the fields documented for this endpoint.
- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: this API key has only the `read` scope. Use a key with `write` access.
`FORBIDDEN`: only an owner or admin may buy credit. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server. An owner can turn it on in the console under Organization › AI apps.
- `409`: `EMAIL_VERIFICATION_REQUIRED`: the member who created this key has not verified their email, where the receipt is sent. Verify it in the console, then retry.
`CREDIT_PURCHASE_UNAVAILABLE`: credit can't be bought for this organization yet. Finish billing setup in the console, or contact support.
`IDEMPOTENCY_CONFLICT`: this `Idempotency-Key` was already used for a different amount. Use a new key for a different purchase.
- `413`: `REQUEST_TOO_LARGE`: the body is larger than 64 KiB. Send a smaller request.
- `422`: `VALIDATION_FAILED`: a field is invalid. Each entry of `errors` names the `field` and its `code`:
`amount_minor` `REQUIRED`: send a positive amount in minor units.
`amount_minor` `AMOUNT_OUT_OF_RANGE`: the amount is outside the per-purchase limits above for your currency. Choose an amount within them.
`UNKNOWN_FIELD`: the body has a field this endpoint does not take (named in `field`). Remove it or fix its spelling.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
`BILLING_UNAVAILABLE`: billing is not answering right now. Wait a few seconds, then retry.
## Get a credit purchase

```http
GET /public/v1/billing/credit/purchases/{purchase_id}
```

One credit purchase: whether it has been paid. `checkout_url` is shown only while the purchase can still be paid. Poll this after *Buy credit* until `status` is `settled` (credit added), or `failed` or `expired` (nothing charged; create a new purchase).

Scope: `read`.

Path parameters:

- `purchase_id`: The purchase's id, e.g. `cpr_...`.

Example:

```sh
curl "https://api.pantechdynamics.com/public/v1/billing/credit/purchases/id_06gk3x9tq7m2v8r4y1c5d0zp8w" \
  -H "Authorization: Bearer $PANTECH_API_KEY"
```

Response `200`:

```json
{
  "id": "id",
  "status": "pending",
  "amount_minor": 1059200,
  "currency": "NGN",
  "method": "checkout",
  "provider": "provider",
  "checkout_url": "checkout-url",
  "checkout_expires_at": "2026-10-03T09:30:00Z",
  "failure_code": null,
  "settled_at": "2026-10-03T09:30:00Z",
  "created_at": "2026-10-03T09:30:00Z",
  "receipt": {
    "id": "id",
    "number": 100
  }
}
```

Errors:

- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `404`: `RESOURCE_NOT_FOUND`: no credit purchase with this id in your organization. Check the id (`cpr_…`).
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
`BILLING_UNAVAILABLE`: billing is not answering right now. Wait a few seconds, then retry.
## List receipts

```http
GET /public/v1/billing/receipts
```

Every receipt issued to your organization, newest first. A receipt is issued, numbered `PTD-RCT-<year>-<number>`, whenever money moves: a card payment, a credit top-up, an invoice paid by card or from credit, a debit from your credit (for example a new instance's first charges), and a refund. A payment's receipt usually appears within a minute of it settling.

Scope: `read`.

Query parameters:

- `limit` (integer): Items per page, 1 to 100. Defaults to 50. From 1 to 100. Defaults to `50`.
- `cursor` (string): The previous page's `next_cursor`, to get the next page. Leave out for the first page; keep the other parameters the same.

Example:

```sh
curl "https://api.pantechdynamics.com/public/v1/billing/receipts" \
  -H "Authorization: Bearer $PANTECH_API_KEY"
```

Response `200`:

```json
{
  "data": [
    {
      "id": "id",
      "number": 100,
      "kind": "payment",
      "issuer_name": "issuer-name",
      "currency": "NGN",
      "amount_minor": 1059200,
      "issued_at": "2026-10-03T09:30:00Z",
      "occurred_at": "2026-10-03T09:30:00Z",
      "description": "description",
      "billed_to": "billed-to",
      "facts": [
        {
          "label": "label",
          "value": "value"
        }
      ],
      "verification": "verification",
      "transaction_id": "transaction-id",
      "pdf_bytes": 1
    }
  ],
  "next_cursor": "eyJhdCI6IjIwMjYtMTAtMDNUMDk6MzA6MDBaIiwiaWQiOiJ2bV8wNmdrIn0"
}
```

Errors:

- `400`: `INVALID_CURSOR`: the cursor did not come from this list. Start again without one.
- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
`BILLING_UNAVAILABLE`: billing is not answering right now. Wait a few seconds, then retry.
## Get a receipt

```http
GET /public/v1/billing/receipts/{receipt_id}
```

One receipt: what it was for, the amount, who issued it and the details printed on it. Download it as a PDF with `GET /public/v1/billing/receipts/{receipt_id}/pdf`.

Scope: `read`.

Path parameters:

- `receipt_id`: The receipt's id, e.g. `rcp_...`.

Example:

```sh
curl "https://api.pantechdynamics.com/public/v1/billing/receipts/id_06gk3x9tq7m2v8r4y1c5d0zp8w" \
  -H "Authorization: Bearer $PANTECH_API_KEY"
```

Response `200`:

```json
{
  "id": "id",
  "number": 100,
  "kind": "payment",
  "issuer_name": "issuer-name",
  "currency": "NGN",
  "amount_minor": 1059200,
  "issued_at": "2026-10-03T09:30:00Z",
  "occurred_at": "2026-10-03T09:30:00Z",
  "description": "description",
  "billed_to": "billed-to",
  "facts": [
    {
      "label": "label",
      "value": "value"
    }
  ],
  "verification": "verification",
  "transaction_id": "transaction-id",
  "pdf_bytes": 1
}
```

Errors:

- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `404`: `RECEIPT_NOT_FOUND`: no receipt with this id in your organization. List your receipts to find it.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
`BILLING_UNAVAILABLE`: billing is not answering right now. Wait a few seconds, then retry.
## Download a receipt

```http
GET /public/v1/billing/receipts/{receipt_id}/pdf
```

The receipt as a PDF, the same document that was emailed. The file name is the receipt number, e.g. `PTD-RCT-2026-000123.pdf`.

Scope: `read`.

Path parameters:

- `receipt_id`: The receipt's id, e.g. `rcp_...`.

Example:

```sh
curl "https://api.pantechdynamics.com/public/v1/billing/receipts/id_06gk3x9tq7m2v8r4y1c5d0zp8w/pdf" \
  -H "Authorization: Bearer $PANTECH_API_KEY"
```

Response `200`: The PDF.

Errors:

- `401`: `UNAUTHENTICATED`: the request has no API key, or the key is mistyped, revoked or expired, or its creator has left the organization. Send `Authorization: Bearer PAN_…` with a valid key; create one in the console under Organization › API keys.
- `403`: `INSUFFICIENT_SCOPE`: the API key lacks the scope this endpoint needs (`read` for GET, `write` for changes; an AI app's key also needs `delete` for a destructive call). Use a key with that scope, or reconnect the AI app with "Manage and delete" access.
`FORBIDDEN`: the member who created the key may not do this in the organization. Use a key created by an owner or admin.
`AI_APPS_DISABLED`: the organization has turned AI app access off, and this request was made with an AI app's key or sent by Pantech's MCP server (`Pantech-Client: mcp/…`). An owner can turn it on in the console under Organization › AI apps.
- `404`: `RECEIPT_NOT_FOUND`: no receipt with this id in your organization. List your receipts to find it.
- `429`: `RATE_LIMITED`: this API key, verified user or organization used its request budget for the current window, or this address exceeded an IP/failed-auth limit. Wait `Retry-After` seconds, then retry. For authenticated quota denials, `RateLimit-Scope` identifies the denying quota and `RateLimit-Reset` reports its remaining window in seconds.
- `503`: `SERVICE_UNAVAILABLE`: request quota storage is unavailable. Wait a few seconds, then retry.
`BILLING_UNAVAILABLE`: billing is not answering right now. Wait a few seconds, then retry.
