Developers
API reference
Every Soca REST endpoint: invoices, plans, payments, subscriptions, events, usage meters and metering, prepaid credits, and entitlements, with parameters, requests, and responses.
The Soca API is a versioned REST surface under /v1. Base URL: https://soca.to. Authenticate every request with Authorization: Bearer sk_test_... or sk_live_.... See Authentication for keys, mode scoping, and rate limits.
- Amounts are USDC base units. USDC on Solana (base units, 6 decimals: $9.00 = 9000000). Send them as an integer or a numeric string; responses return them as strings.
- Field names are snake_case in both requests and responses. Responses carry a
wc-versionheader (currently2026-07-01). - List endpoints share one envelope:
{ "object": "list", "data": [...], "has_more": true, "next_cursor": "2026-07-17T12:00:00.000Z" }. Page with?limit=(1–100, default 25) and?before=(thenext_cursorfrom the previous page). A pagination recipe is in Errors and testing. - Errors share one envelope:
{ "error": { "code": "...", "message": "..." } }. The full code table is in Errors and testing. - Creation settles to your on-file wallet. The destination wallet is never accepted over the API. Changing it requires a fresh signature in the dashboard.
Prefer typed calls? Every endpoint here has a 1:1 method in the official TypeScript client. See The Soca SDK.
Payments objects
Create a one-time invoice. Returns a hosted, non-custodial checkout_url to send the customer; no money moves until they pay, then an invoice.paid webhook fires.
| Parameter | Type | Required | Description |
|---|---|---|---|
| amount | integer or string | Yes | USDC base units: 2500000 = 2.50 USDC. Must be a positive integer. |
| description | string | Yes | Shown on the hosted checkout page. 1–500 characters. |
| memo | string | No | Free-form note, up to 200 characters. |
| invoice_number | string | No | Your own reference, up to 100 characters. |
| recipient | string | No | Who it is billed to: a label or email for your records, up to 200 characters. |
| due_at | string | No | ISO-8601 due date. |
List invoices, newest first. Standard list envelope with cursor pagination.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | 1–100, default 25. |
| before | string | No | ISO timestamp cursor. Pass the previous page's next_cursor. |
Create a subscription plan (product + price + platform-signed on-chain plan). Returns a hosted checkout_url for subscribers; the first successful charge fires subscription.active. Minimum price is 1.00 USDC. Plans are create-only; there is no GET /v1/plans yet.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | The plan/product name, 2–80 characters. |
| amount | integer or string | Yes | USDC base units charged per period. Minimum 1000000 (1.00 USDC); below that returns 400 amount_too_small. |
| interval | string | Yes | One of week, biweekly, month, year, custom. |
| interval_days | integer | No | Required when interval is custom: days per billing period, 1–365. |
| description | string | No | Optional, up to 300 characters. |
| usage_type | string | No | licensed (default) charges the fixed amount each period. metered bills the customer’s usage each period instead, and amount becomes the per-period cap (the most that can be pulled). Metered plans take no charge at subscribe. |
| meter | string | No | Required when usage_type is metered: the meter id (from POST /v1/meters or GET /v1/meters) whose per-period usage is billed. |
| pricing_model | object | No | Required when usage_type is metered: how usage is priced. One of {"kind":"per_unit","unitAmount":100,"includedUnits":0}, {"kind":"package","unitAmount":...,"packageSize":...}, {"kind":"tiered","mode":"graduated"|"volume","tiers":[{"upTo":1000,"unitAmount":...},{"upTo":null,"unitAmount":...}]}, or {"kind":"flat","amount":...}. Amounts are USDC base units; the last tier’s upTo must be null. |
List confirmed on-chain payments, newest first.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | 1–100, default 25. |
| before | string | No | ISO timestamp cursor from next_cursor. |
List subscriptions, newest first.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | 1–100, default 25. |
| before | string | No | ISO timestamp cursor from next_cursor. |
List the event feed backing your webhooks, newest first. Poll it or reconcile deliveries against it. Event types are listed in Webhooks and events.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | 1–100, default 25. |
| before | string | No | ISO timestamp cursor from next_cursor. |
Usage billing
Meters are defined in the dashboard; your backend streams usage against them by event_name. Customers can be referenced by Soca customer_id or by external_customer_id: your own stable identifier, which Soca upserts into a customer record on first sight.
Create a meter — the named thing usage is recorded against. Its key is the event_name you send events with. Idempotent by key (a repeat returns the existing meter). Create a meter before a metered plan or a metered feature can reference it.
| Parameter | Type | Required | Description |
|---|---|---|---|
| key | string | Yes | The event name. Letters, numbers, dot, dash, underscore. Unique per account + mode. |
| display_name | string | Yes | Human name shown in the dashboard. |
| aggregation | string | No | How events roll into a period total: sum (default), count, max, or last. |
| event_value_key | string | No | The event property holding the quantity. Omit and each event counts as 1 (use with count). |
| unit_label | string | No | Optional display unit, e.g. "tokens", "requests". |
| description | string | No | Optional. |
Create a feature — a capability you gate with entitlements. boolean (has it or not) or metered (has it, up to an allowance enforced against a meter). Idempotent by key. Create a feature before granting or checking it.
| Parameter | Type | Required | Description |
|---|---|---|---|
| key | string | Yes | The key you check access by. Letters, numbers, dot, dash, underscore. |
| name | string | Yes | Human name. |
| type | string | No | boolean (default) or metered. |
| meter | string | No | Required for a metered feature: the meter id whose usage counts against the allowance. |
| unit_label | string | No | Optional. |
| description | string | No | Optional. |
List your usage meters, newest first. Standard list envelope.
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | 1–100, default 25. |
| before | string | No | ISO timestamp cursor from next_cursor. |
Ingest usage: one event, or a batch of up to 500 as { "events": [...] }. Idempotent per idempotency_key (a body field per event, or an Idempotency-Key header for single events): retrying with the same key returns 200 with deduplicated: true instead of double-counting. The meter must exist first, or you get 404 unknown_meter. Rate limit: 600 requests/min.
| Parameter | Type | Required | Description |
|---|---|---|---|
| event_name | string | Yes | The meter's event name, as defined in the dashboard. |
| value | integer or string | No | Non-negative usage amount. Required for most meters; count-aggregated meters default each event to 1, while other aggregations return 400 missing_value without it. |
| customer_id | string | No | The Soca customer the usage belongs to. |
| external_customer_id | string | No | Your own stable customer identifier, which Soca upserts into a customer automatically. |
| timestamp | string | No | ISO-8601 event time. Defaults to now. |
| idempotency_key | string | No | Dedupe key, up to 255 characters. Safe to retry with the same key. |
| properties | object | No | Arbitrary JSON metadata stored with the event. |
Credits
Prepaid balances per customer, backed by an immutable ledger. Grant credits in, debit them as usage happens, and read the balance back. Grants and debits are idempotent per idempotency_key (body field or Idempotency-Key header).
Top up a customer's prepaid credit balance. One of customer_id / external_customer_id is required (400 customer_required otherwise). Emits a credit.granted event.
| Parameter | Type | Required | Description |
|---|---|---|---|
| amount | integer or string | Yes | USDC base units to add to the balance. Must be positive. |
| customer_id | string | No | The Soca customer to credit. |
| external_customer_id | string | No | Your own stable customer identifier, upserted automatically. |
| reason | string | No | Ledger note, up to 300 characters. Defaults to "API grant". |
| expires_at | string | No | ISO-8601 expiry for the granted credits. |
| idempotency_key | string | No | Dedupe key, up to 255 characters. |
Draw down a customer's prepaid credits for usage. By default the debit covers only what the balance allows and returns the remainder as shortfall for you to bill separately; pass allow_negative: true to let the balance go below zero instead. The customer must already have a credit account (404 no_credit_account otherwise). Emits a credit.debited event. Rate limit: 120 requests/min.
| Parameter | Type | Required | Description |
|---|---|---|---|
| amount | integer or string | Yes | USDC base units to draw down. Must be positive. |
| customer_id | string | No | The Soca customer to debit. |
| external_customer_id | string | No | Your own stable customer identifier. |
| reason | string | No | Ledger note, up to 300 characters. Defaults to "API debit". |
| allow_negative | boolean | No | Allow the balance to go negative instead of reporting a shortfall. Default false. |
| idempotency_key | string | No | Dedupe key, up to 255 characters. |
The customer's current prepaid balance plus ledger totals. A customer with no credit account returns a plain zero balance. Rate limit: 240 requests/min.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The Soca customer id (path parameter). Unknown ids return 404 unknown_customer. |
Entitlements
Entitlements map what a customer has paid for to what your product should let them do. Features are defined in the dashboard by feature_key; grants come from plans automatically or from this API manually; your backend asks the check endpoint before serving a feature.
Grant a feature to a customer, optionally with a usage limit and an expiry. The feature must exist (404 unknown_feature), and one of customer_id / external_customer_id is required. Emits an entitlement.granted event.
| Parameter | Type | Required | Description |
|---|---|---|---|
| feature_key | string | Yes | The feature to grant, as defined in the dashboard. |
| customer_id | string | No | The Soca customer to grant to. |
| external_customer_id | string | No | Your own stable customer identifier, upserted automatically. |
| limit_value | integer or string | No | For metered features: the usage limit (non-negative integer). |
| active_until | string | No | ISO-8601 timestamp for when the entitlement lapses. |
The access-control check your backend calls before serving a feature answers two questions: is this customer entitled right now, and for metered features, how much of the limit remains? reason is one of active, grace, no_grant, not_started, revoked, expired, limit_exceeded. Built for the hot path: 600 requests/min.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The Soca customer id (path parameter). Unknown ids return 404 unknown_customer. |
| feature_key | string | Yes | The feature to check (path parameter). Unknown keys return 404 unknown_feature. |
Anything not listed here does not exist yet. GET /v1/{resource} on an unknown resource returns 404 resource_not_found; POSTing to a read-only resource (payments, subscriptions, events, meters) returns 405 method_not_allowed.
Was this page helpful?
A quick signal helps us improve the right pages.