ISV credit management APIs
⚠️ Internal sandbox preview — not approved for partner or production use. Credit reads and writes do not yet share one environment model (see Environment below). Do not integrate against production credit data until Complyance confirms the environment contract.
Use the Connect V3 credit APIs to inspect an ISV credit pool, manage company allowances, submit bulk changes, and reconcile activity through the immutable credit ledger.
An allowance is a commercial limit for a company. Creating or changing an allowance does not transfer, reserve, or consume credits from the ISV pool. Credits are consumed only by approved Complyance workflows.
Endpoints
| Operation | Method | Endpoint |
|---|---|---|
| List allowances | GET | /v3/connect/credits/allowances |
| Read one company's credit state | GET | /v3/connect/credits/allowances/{companyId} |
| Pause active allowance | POST | /v3/connect/credits/allowances/{companyId}/pause |
| Create or replace scheduled allowance | PUT | /v3/connect/credits/allowances/{companyId}/scheduled |
| Submit bulk changes | POST | /v3/connect/credits/allowances/bulk |
| Read ledger | GET | /v3/connect/credits/transactions |
| Read ISV pool | GET | /v3/connect/credits/pool |
| Resume active allowance | POST | /v3/connect/credits/allowances/{companyId}/resume |
| Read bulk job | GET | /v3/connect/credits/allowances/bulk/{jobId} |
| Create or replace active allowance | PUT | /v3/connect/credits/allowances/{companyId}/current |
| Cancel scheduled allowance | DELETE | /v3/connect/credits/allowances/{companyId}/scheduled |
| Read ledger export status | GET | /v3/connect/credits/exports/{exportId} |
| Request ledger export | POST | /v3/connect/credits/exports |
There is no public debit, refund, reversal, reservation, finalization, or credit-purchase endpoint.
In the examples, replace {BASE_URL}, {API_KEY}, and the other brace-delimited placeholders with your values. {BASE_URL} is https://prod.gets.complyance.io for the deployed platform, or http://localhost:3003 for local development. All example IDs, timestamps, limits, balances, and usage values are illustrative.
Authentication
Send the ISV workspace API key in the X-API-Key header:
X-API-Key: {API_KEY}An API key may alternatively be sent as a Bearer token:
Authorization: Bearer {API_KEY}Keep API keys secret. Never include a real API key in documentation, browser code, logs, or source control.
Credit endpoints are scoped to the workspace associated with the credential.
Environment
Environment behaviour currently differs between reads and writes:
- Writes — pause, resume, scheduled create/replace/cancel, current create/replace, and bulk jobs — read the
X-Environmentheader and store the result against that data environment. Accepted values aresandbox,test, anddevelopment(all resolve to sandbox data), andproductionandprod(production data). Any other value returns400 CONNECT_INVALID_ENVIRONMENT. - When
X-Environmentis omitted on a write, it defaults toproduction. Always send the header explicitly on every write, and usesandboxunless production credit management has been confirmed with Complyance. - Reads — pool, allowance list, single-company state, ledger, and exports — do not accept an environment and are not environment-filtered. A read may therefore not reflect the environment a write targeted.
Because of this asymmetry, these APIs are an internal sandbox preview and are not approved for partner or production use. This section will be updated when reads and writes share one environment model.
Common headers
| Header | Required | Description |
|---|---|---|
X-API-Key | Yes | API key for an eligible ISV partner workspace. Use this or Authorization, not both. |
X-Environment | For writes | Data environment for allowance writes and bulk jobs: sandbox or production. Omitted defaults to production — always set it explicitly. Read endpoints ignore this header. |
Accept | No | Set to application/json. |
Content-Type | For requests with a body | Set to application/json. |
Idempotency-Key | For writes and export requests | Unique key identifying the intended operation. Reusing the key with the same payload returns the original result. |
If-None-Match | For first allowance creation | Use * when no current or scheduled resource exists. |
If-Match | For updates and state changes | Quoted resource version, for example "3". |
X-Correlation-Id | No | Identifier used to correlate direct allowance writes and bulk jobs with your system. It is ignored on reads and export requests. |
Access
- Workspace API keys can read and write credit resources for their own ISV workspace.
- Authenticated Owners, Admins, and Partners can perform allowance writes.
- Other authenticated workspace members have read-only access.
- Every
companyIdmust be the canonical 24-character hexadecimal company ID and must belong to the authenticated ISV workspace. - Internal onboarding and workspace identifiers are not accepted as public company references.
- Allowance writes and allowance-list results are limited to active managed companies. The single-company state, ledger, and export endpoints can read historical data for an inactive company.
Response envelope
Successful responses use this envelope:
{
"success": true,
"data": {},
"meta": {
"requestId": "01K1EXAMPLE000000000000000",
"timestamp": "2026-08-12T10:00:00.000Z"
}
}After authentication succeeds, Connect validation and service errors use this envelope:
{
"success": false,
"error": {
"code": "CONNECT_ALLOWANCE_WRITE_PRECONDITION_FAILED",
"message": "Unable to complete allowance write."
},
"meta": {
"requestId": "01K1EXAMPLE000000000000000",
"timestamp": "2026-08-12T10:00:00.000Z"
}
}Use meta.requestId when contacting support.
Authentication can fail before the Connect route handles the request. Missing, invalid, or revoked credentials can therefore return a plain-text 401 Unauthorized response instead of the JSON error envelope. Check the HTTP status and Content-Type before parsing an error body as JSON.
Allowance lifecycle
A company can have one current allowance and at most one scheduled allowance.
| Status | Meaning |
|---|---|
scheduled | Future allowance waiting for its startAt. |
active | Current allowance permits consumption while capacity remains. |
paused | New consumption is blocked without changing the period dates. |
expired | The allowance period has ended. |
superseded | A scheduled allowance activated and replaced the prior current allowance. |
Allowance periods use an inclusive startAt and an exclusive endAt. Send UTC ISO 8601 timestamps, such as 2030-01-01T00:00:00.000Z. periodType is classification metadata; it does not calculate dates or renew an allowance automatically.
Replacing a current allowance keeps its existing allowancePeriodId, startAt, finalized usage, and active reservations. The new limit cannot be lower than finalized usage plus active reservations.
Concurrency and idempotency
Every allowance write requires an Idempotency-Key and a resource precondition:
- To create a resource, send
If-None-Match: *. - To replace a resource, send
If-Match: "{version}"using the latest version. - To pause, resume, or cancel, send
If-Match: "{version}". - Read the latest versions from the response body and
ETagheaders.
Do not send If-None-Match and If-Match together. Reusing an idempotency key with a different payload returns a conflict.
Resource schemas
Allowance
{
"limit": 1000,
"periodType": "monthly",
"planReference": "plan-pro-2026",
"metadata": {
"subscriptionId": "sub_123"
},
"allowancePeriodId": "period_01",
"startAt": "2026-08-01T00:00:00.000Z",
"endAt": "2026-09-01T00:00:00.000Z",
"status": "active",
"version": 3,
"finalizedUsage": 250,
"reserved": 20,
"updatedAt": "2026-08-12T10:00:00.000Z"
}Company credit state
{
"companyId": "{COMPANY_ID}",
"current": {},
"scheduled": null,
"usage": 250,
"reserved": 20,
"available": 730,
"effectiveAvailability": 730,
"blockReason": null,
"warnings": [],
"asOf": "2026-08-12T10:00:00.000Z",
"lastChangedAt": "2026-08-12T09:00:00.000Z"
}current and scheduled contain an Allowance object or null.
Warning values
| Warning | Meaning |
|---|---|
COMPANY_THRESHOLD_REACHED | Finalized usage plus reservations reached at least 80% of the current allowance. |
OVER_ALLOCATION | Total current and scheduled allowances exceed the available ISV pool. The write still succeeds. |
EFFECTIVE_AVAILABILITY_LIMITED_BY_POOL | The ISV pool is lower than the remaining company allowance. |
POOL_LOW | Available pool credits reached the low threshold. |
POOL_CRITICAL | Available pool credits reached the critical threshold. |
POOL_EXHAUSTED | No pool credits are available. |
Block reasons
| Block reason | Meaning |
|---|---|
NO_ACTIVE_ALLOWANCE | The company has no current active allowance. |
ALLOWANCE_PAUSED | The current allowance is paused. |
ALLOWANCE_INACTIVE | The current allowance is not active. |
ALLOWANCE_EXHAUSTED | Finalized usage plus reservations exhausted the company allowance. |
POOL_EXHAUSTED | The ISV pool is exhausted. |
1. List allowances
Returns a cursor-paginated snapshot of active managed companies' credit states.
GET /v3/connect/credits/allowancesQuery parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
cursor | string | No | Opaque cursor returned by the preceding page. |
limit | integer | No | Number of results from 1 to 100. Defaults to 25. |
companyId | string | No | Return one managed company. |
status | string | No | Filter by current or scheduled allowance status. |
countryCode | string | No | Filter companies by country. |
blockReason | string | No | Filter by block reason. |
effectiveFrom | date-time | No | Include allowances overlapping the period beginning at this time. |
effectiveTo | date-time | No | Include allowances overlapping the period ending at this time. Must be later than effectiveFrom. |
Inactive and archived companies are omitted, including when companyId is supplied. Use Read one company's credit state to inspect retained credit history for an inactive company.
Request
curl --get '{BASE_URL}/v3/connect/credits/allowances' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json' \
--data-urlencode 'countryCode=AE' \
--data-urlencode 'limit=25'Response
200 OK
{
"success": true,
"data": {
"items": [
{
"companyId": "{COMPANY_ID}",
"current": {
"limit": 1000,
"periodType": "monthly",
"allowancePeriodId": "period_01",
"startAt": "2026-08-01T00:00:00.000Z",
"endAt": "2026-09-01T00:00:00.000Z",
"status": "active",
"version": 3,
"finalizedUsage": 250,
"reserved": 20
},
"scheduled": null,
"usage": 250,
"reserved": 20,
"available": 730,
"effectiveAvailability": 730,
"blockReason": null,
"warnings": [],
"asOf": "2026-08-12T10:00:00.000Z",
"lastChangedAt": "2026-08-12T09:00:00.000Z"
}
],
"nextCursor": null,
"asOf": "2026-08-12T10:00:00.000Z"
},
"meta": {
"requestId": "01K1EXAMPLE000000000000000",
"timestamp": "2026-08-12T10:00:00.000Z"
}
}Pass data.nextCursor as cursor to request the next page. A null cursor means there are no more results.
2. Read one company's credit state
Returns the current allowance, scheduled allowance, usage, reservations, effective availability, warnings, and recent ledger activity for one company.
GET /v3/connect/credits/allowances/{companyId}Request
curl --location '{BASE_URL}/v3/connect/credits/allowances/{COMPANY_ID}' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json'Response
200 OK
The response data uses the Company credit state schema and adds:
| Field | Type | Description |
|---|---|---|
recentActivity | array | Up to 10 recent ledger transactions for the company. |
scheduledEtag | string or null | Quoted scheduled allowance version, for example "4". |
The response includes an ETag header containing the quoted current allowance version. When no current allowance exists, the value is "0".
3. Pause active allowance
Blocks new consumption without changing the allowance dates or deleting its history.
POST /v3/connect/credits/allowances/{companyId}/pauseRequired headers
Idempotency-Key: pause-{UNIQUE_KEY}
If-Match: "{CURRENT_VERSION}"
Content-Type: application/jsonRequest
curl --request POST '{BASE_URL}/v3/connect/credits/allowances/{COMPANY_ID}/pause' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: pause-{UNIQUE_KEY}' \
--header 'If-Match: "3"' \
--data '{
"reason": "Commercial subscription suspended"
}'reason is required and may contain up to 500 characters.
Response
200 OK
{
"success": true,
"data": {
"companyId": "{COMPANY_ID}",
"current": {
"limit": 1000,
"periodType": "monthly",
"allowancePeriodId": "period_01",
"startAt": "2026-08-01T00:00:00.000Z",
"endAt": "2026-09-01T00:00:00.000Z",
"status": "paused",
"version": 4,
"finalizedUsage": 250,
"reserved": 20
},
"replayed": false
},
"meta": {
"requestId": "01K1EXAMPLE000000000000000",
"timestamp": "2026-08-12T10:00:00.000Z"
}
}The response ETag contains the new current version.
4. Create or replace scheduled allowance
Creates or replaces the single future allowance for a company. At its inclusive startAt, the allowance becomes eligible for activation and receives a new allowancePeriodId when activated.
Activation is asynchronous. A background lifecycle worker currently checks due allowances every 60 seconds, so the allowance can remain scheduled briefly after startAt.
PUT /v3/connect/credits/allowances/{companyId}/scheduledRequired headers
For the first scheduled allowance:
Idempotency-Key: schedule-{UNIQUE_KEY}
If-None-Match: *
Content-Type: application/jsonTo replace the scheduled allowance:
Idempotency-Key: schedule-{UNIQUE_KEY}
If-Match: "{SCHEDULED_VERSION}"
Content-Type: application/jsonRequest body
| Field | Type | Required | Description |
|---|---|---|---|
limit | number | Yes | Allowance limit. Must be 0 or greater. |
periodType | string | Yes | monthly, annual, or custom. |
startAt | date-time | Yes | Future UTC timestamp. |
endAt | date-time | Yes | UTC timestamp later than startAt. |
reason | string | Yes | Reason for the change, up to 500 characters. |
planReference | string | No | ISV plan or subscription reference, up to 500 characters. |
metadata | object | No | Partner metadata associated with the allowance. |
Request
curl --request PUT '{BASE_URL}/v3/connect/credits/allowances/{COMPANY_ID}/scheduled' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: schedule-{UNIQUE_KEY}' \
--header 'If-None-Match: *' \
--data '{
"limit": 1500,
"periodType": "monthly",
"startAt": "{FUTURE_PERIOD_START_UTC}",
"endAt": "{FUTURE_PERIOD_END_UTC}",
"reason": "Subscription renewal",
"planReference": "plan-enterprise"
}'Response
201 Createdwhen usingIf-None-Match: *.200 OKwhen replacing a scheduled allowance.
{
"success": true,
"data": {
"companyId": "{COMPANY_ID}",
"scheduled": {
"limit": 1500,
"periodType": "monthly",
"planReference": "plan-enterprise",
"startAt": "2030-01-01T00:00:00.000Z",
"endAt": "2030-02-01T00:00:00.000Z",
"status": "scheduled",
"version": 1,
"finalizedUsage": 0,
"reserved": 0
},
"replayed": false
},
"meta": {
"requestId": "01K1EXAMPLE000000000000000",
"timestamp": "2026-08-12T10:00:00.000Z"
}
}The response ETag contains the scheduled allowance version.
5. Submit bulk changes
Submits an asynchronous job containing up to 500 allowance operations. Each row is processed independently, so one row failure does not roll back unrelated successful rows.
POST /v3/connect/credits/allowances/bulkRequired headers
Idempotency-Key: bulk-{UNIQUE_KEY}
Content-Type: application/jsonSupported operations
| Operation | Target | Minimum expectedVersion |
|---|---|---|
replace_current | Current allowance | 0; use 0 to create. |
replace_scheduled | Scheduled allowance | 0; use 0 to create. |
cancel_scheduled | Scheduled allowance | 1 |
pause | Current allowance | 1 |
resume | Current allowance | 1 |
Replacement rows also require limit, periodType, startAt, and endAt. Action rows contain only operationId, companyId, operation, expectedVersion, and reason.
Within one job:
operationIdmust be unique and contain no more than 200 characters.- A company may have only one operation for each target: current or scheduled.
- Scheduled allowance start times must be in the future.
startAtandendAtmust be UTC timestamps ending inZ.- Undocumented fields are rejected.
Request
curl --request POST '{BASE_URL}/v3/connect/credits/allowances/bulk' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: bulk-{UNIQUE_KEY}' \
--data '{
"operations": [
{
"operationId": "op-001",
"companyId": "{COMPANY_ID_1}",
"operation": "replace_current",
"expectedVersion": 3,
"reason": "Plan upgrade",
"limit": 2000,
"periodType": "monthly",
"startAt": "{CURRENT_PERIOD_START_UTC}",
"endAt": "{CURRENT_PERIOD_END_UTC}",
"planReference": "plan-enterprise"
},
{
"operationId": "op-002",
"companyId": "{COMPANY_ID_2}",
"operation": "pause",
"expectedVersion": 2,
"reason": "Subscription suspended"
}
]
}'Response
202 Accepted
{
"success": true,
"data": {
"jobId": "{JOB_ID}",
"status": "accepted",
"replayed": false,
"totals": {
"total": 2,
"processed": 0,
"succeeded": 0,
"failed": 0
},
"results": [
{
"operationId": "op-001",
"companyId": "{COMPANY_ID_1}",
"operation": "replace_current",
"status": "pending"
},
{
"operationId": "op-002",
"companyId": "{COMPANY_ID_2}",
"operation": "pause",
"status": "pending"
}
],
"createdAt": "2026-08-12T10:00:00.000Z",
"updatedAt": "2026-08-12T10:00:00.000Z"
},
"meta": {
"requestId": "01K1EXAMPLE000000000000000",
"timestamp": "2026-08-12T10:00:00.000Z"
}
}Poll the Read bulk job endpoint until the job reaches completed, completed_with_errors, or failed.
6. Read ledger
Returns cursor-paginated immutable credit activity for the authenticated workspace.
GET /v3/connect/credits/transactionsQuery parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
cursor | string | No | Opaque cursor from the preceding page. |
limit | integer | No | Number of records from 1 to 100. |
companyId | string | No | Filter by managed company. |
eventType | string | No | Filter by one supported ledger event type. |
occurredFrom | date-time | No | Include events at or after this timestamp. |
occurredTo | date-time | No | Include events at or before this timestamp. Cannot precede occurredFrom. |
correlationId | string | No | Filter by correlation ID. |
idempotencyKey | string | No | Filter by idempotency key. |
requestId | string | No | Filter by Connect request ID. |
transactionId | string | No | Filter by ledger transaction ID. |
bulkJobId | string | No | Filter by bulk job ID. |
Supported eventType values are:
allowance.created
allowance.replaced
allowance.scheduled
allowance.cancelled
allowance.activated
allowance.expired
allowance.superseded
allowance.paused
allowance.resumed
reservation.created
reservation.released
reservation.needs_reconciliation
consumption.finalized
consumption.reversed
correction.created
credit.issued
credit.expiredRequest
curl --get '{BASE_URL}/v3/connect/credits/transactions' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json' \
--data-urlencode 'companyId={COMPANY_ID}' \
--data-urlencode 'limit=25'Response
200 OK
{
"success": true,
"data": {
"items": [
{
"transactionId": "txn_01",
"eventType": "allowance.replaced",
"occurredAt": "2026-08-12T09:00:00.000Z",
"companyId": "{COMPANY_ID}",
"amount": 1000,
"reason": "Plan upgrade",
"correlationId": "subscription-change-123",
"idempotencyKey": "allowance-123",
"requestId": "01K1EXAMPLE000000000000000",
"actor": {
"type": "api_key",
"actorId": "key_01"
},
"balanceBefore": {},
"balanceAfter": {},
"metadata": {
"version": 3,
"status": "active"
}
}
],
"nextCursor": null,
"asOf": "2026-08-12T10:00:00.000Z"
},
"meta": {
"requestId": "01K1EXAMPLE000000000000000",
"timestamp": "2026-08-12T10:00:00.000Z"
}
}A ledger item can also include bulkJobId, originalTransactionId, countryCode, and documentType when relevant.
7. Read ISV pool
Returns the authenticated ISV workspace's current credit-pool snapshot.
GET /v3/connect/credits/poolRequest
curl --location '{BASE_URL}/v3/connect/credits/pool' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json'Response
200 OK
{
"success": true,
"data": {
"active": 10000,
"reserved": 200,
"consumed": 3000,
"expired": 100,
"available": 6800,
"lots": [
{
"active": 10000,
"available": 6800,
"expiresAt": "2027-08-01T00:00:00.000Z"
}
],
"asOf": "2026-08-12T10:00:00.000Z",
"warnings": []
},
"meta": {
"requestId": "01K1EXAMPLE000000000000000",
"timestamp": "2026-08-12T10:00:00.000Z"
}
}8. Resume active allowance
Allows new consumption against a paused allowance when the allowance period has not ended.
POST /v3/connect/credits/allowances/{companyId}/resumeRequest
curl --request POST '{BASE_URL}/v3/connect/credits/allowances/{COMPANY_ID}/resume' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: resume-{UNIQUE_KEY}' \
--header 'If-Match: "4"' \
--data '{
"reason": "Subscription restored"
}'Response
200 OK
The response has the same shape as Pause active allowance. data.current.status is active, and the response ETag contains the new version.
9. Read bulk job
Returns the current job state and stable row-level outcomes.
GET /v3/connect/credits/allowances/bulk/{jobId}Request
curl --location '{BASE_URL}/v3/connect/credits/allowances/bulk/{JOB_ID}' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json'Response
200 OK
The response uses the bulk-job schema returned by Submit bulk changes. Each row has one of these statuses:
| Status | Meaning |
|---|---|
pending | The row is waiting to be processed. |
processing | The row is being processed. |
succeeded | The operation completed successfully. |
failed | The operation failed; inspect the row's error.code and error.message. |
Job status is accepted, processing, completed, completed_with_errors, or failed. An unknown or cross-workspace job returns 404 CONNECT_CREDIT_BULK_JOB_NOT_FOUND.
10. Create or replace active allowance
Creates the first active allowance or fully replaces the current allowance representation.
PUT /v3/connect/credits/allowances/{companyId}/currentRequired headers
For the first current allowance:
Idempotency-Key: current-{UNIQUE_KEY}
If-None-Match: *
Content-Type: application/jsonTo replace the current allowance:
Idempotency-Key: current-{UNIQUE_KEY}
If-Match: "{CURRENT_VERSION}"
Content-Type: application/jsonRequest
curl --request PUT '{BASE_URL}/v3/connect/credits/allowances/{COMPANY_ID}/current' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: current-{UNIQUE_KEY}' \
--header 'If-None-Match: *' \
--data '{
"limit": 1000,
"periodType": "monthly",
"startAt": "{CURRENT_PERIOD_START_UTC}",
"endAt": "{CURRENT_PERIOD_END_UTC}",
"reason": "Initial subscription allowance",
"planReference": "plan-pro"
}'startAt cannot be in the future, endAt must be later than startAt, and endAt must be in the future. When replacing an existing current allowance, send the existing startAt unchanged.
Response
201 Createdwhen the current allowance is created.200 OKwhen the current allowance is replaced.
{
"success": true,
"data": {
"companyId": "{COMPANY_ID}",
"current": {
"limit": 1000,
"periodType": "monthly",
"planReference": "plan-pro",
"allowancePeriodId": "period_01",
"startAt": "2026-08-01T00:00:00.000Z",
"endAt": "2026-09-01T00:00:00.000Z",
"status": "active",
"version": 1,
"finalizedUsage": 0,
"reserved": 0
},
"replayed": false
},
"meta": {
"requestId": "01K1EXAMPLE000000000000000",
"timestamp": "2026-08-12T10:00:00.000Z"
}
}The response ETag contains the current allowance version.
11. Cancel scheduled allowance
Cancels the future allowance before it activates. Current allowance state is unchanged.
DELETE /v3/connect/credits/allowances/{companyId}/scheduledRequest
curl --request DELETE '{BASE_URL}/v3/connect/credits/allowances/{COMPANY_ID}/scheduled' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: cancel-schedule-{UNIQUE_KEY}' \
--header 'If-Match: "1"' \
--data '{
"reason": "Renewal was cancelled"
}'Response
200 OK
{
"success": true,
"data": {
"companyId": "{COMPANY_ID}",
"cancelled": true,
"version": 1,
"replayed": false
},
"meta": {
"requestId": "01K1EXAMPLE000000000000000",
"timestamp": "2026-08-12T10:00:00.000Z"
}
}The response ETag contains the cancelled scheduled allowance's existing version. Cancellation does not increment that version.
12. Read ledger export status
Returns the state of an asynchronous CSV export job. A completed export includes a temporary download URL.
GET /v3/connect/credits/exports/{exportId}Request
curl --location '{BASE_URL}/v3/connect/credits/exports/{EXPORT_ID}' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json'Response
200 OK
{
"success": true,
"data": {
"exportId": "{EXPORT_ID}",
"status": "completed",
"format": "csv",
"asOf": "2026-08-12T10:00:00.000Z",
"createdAt": "2026-08-12T10:00:00.000Z",
"completedAt": "2026-08-12T10:01:00.000Z",
"expiresAt": "2026-08-13T10:01:00.000Z",
"downloadUrl": "https://example.invalid/temporary-download-url",
"rowCount": 125
},
"meta": {
"requestId": "01K1EXAMPLE000000000000000",
"timestamp": "2026-08-12T10:01:00.000Z"
}
}Export status is accepted, processing, completed, failed, or expired. A failed job can include errorCode; an idempotent replay can include replayed.
13. Request ledger export
Creates an asynchronous, consistent-snapshot CSV export using the supplied ledger filters.
POST /v3/connect/credits/exportsRequired headers
Idempotency-Key: export-{UNIQUE_KEY}
Content-Type: application/jsonRequest body
format must be csv. The optional filters object accepts the same fields as the ledger endpoint except cursor and limit.
Request
curl --request POST '{BASE_URL}/v3/connect/credits/exports' \
--header 'X-API-Key: {API_KEY}' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: export-{UNIQUE_KEY}' \
--data '{
"format": "csv",
"filters": {
"companyId": "{COMPANY_ID}",
"occurredFrom": "2026-08-01T00:00:00.000Z",
"occurredTo": "2026-08-31T23:59:59.999Z"
}
}'Response
202 Accepted
{
"success": true,
"data": {
"exportId": "{EXPORT_ID}",
"status": "accepted",
"format": "csv",
"asOf": "2026-08-12T10:00:00.000Z",
"createdAt": "2026-08-12T10:00:00.000Z",
"completedAt": null,
"expiresAt": null,
"downloadUrl": null,
"replayed": false
},
"meta": {
"requestId": "01K1EXAMPLE000000000000000",
"timestamp": "2026-08-12T10:00:00.000Z"
}
}Poll the Read ledger export status endpoint until the job reaches a terminal state.
Common errors
The following table lists common errors. An endpoint can also return a more specific code describing the failed operation.
| HTTP status | Example code | Description |
|---|---|---|
400 | CONNECT_VALIDATION_ERROR | A path, query, header, or body field is missing or malformed. |
400 | CONNECT_ALLOWANCE_INVALID_COMMAND | Required idempotency or concurrency headers are missing or invalid. |
400 | CONNECT_ALLOWANCE_INVALID_CURSOR | An allowance pagination cursor is invalid. |
400 | CONNECT_ALLOWANCE_INVALID_EFFECTIVE_PERIOD | Allowance effective-period filters are invalid. |
400 | CONNECT_ALLOWANCE_WRITE_LIMIT_BELOW_COMMITTED_USAGE | The new limit is below finalized usage plus active reservations. |
400 | CONNECT_ALLOWANCE_WRITE_START_AT_IMMUTABLE | A replacement attempted to change the current period's startAt. |
400 | CONNECT_SCHEDULED_ALLOWANCE_WRITE_INVALID_PERIOD | Scheduled timestamps are invalid or the start time is not in the future. |
400 | CONNECT_CREDIT_BULK_UNSUPPORTED_FIELD | A bulk row contains a field that is not part of its operation schema. |
400 | CONNECT_CREDIT_LEDGER_INVALID_CURSOR | A ledger pagination cursor is invalid. |
400 | CONNECT_CREDIT_LEDGER_INVALID_PERIOD | occurredTo precedes occurredFrom. |
400 | CONNECT_CREDIT_LEDGER_INVALID_EVENT_TYPE | eventType is not supported. |
400 | CONNECT_CREDIT_EXPORT_IDEMPOTENCY_KEY_REQUIRED | The export request is missing Idempotency-Key. |
401 | CONNECT_AUTH_CONTEXT_MISSING | Authentication did not provide a workspace context. |
403 | CONNECT_ISV_ACCESS_DENIED | The workspace is not eligible for Connect ISV APIs. |
403 | CONNECT_FORBIDDEN_INSUFFICIENT_ROLE | The authenticated user cannot perform credit writes. |
404 | CONNECT_COMPANY_NOT_FOUND | The company is missing or is not visible to the authenticated workspace. |
404 | CONNECT_CREDIT_BULK_JOB_NOT_FOUND | The bulk job is missing or belongs to another workspace. |
404 | CONNECT_CREDIT_LEDGER_EXPORT_NOT_FOUND | The export is missing or belongs to another workspace. |
409 | CONNECT_ALLOWANCE_WRITE_IDEMPOTENCY_CONFLICT | An idempotency key was reused with a different request. |
409 | CONNECT_SCHEDULED_ALLOWANCE_WRITE_IDEMPOTENCY_CONFLICT | A scheduled-write idempotency key was reused with a different request. |
409 | CONNECT_SCHEDULED_ALLOWANCE_CANCEL_IDEMPOTENCY_CONFLICT | A cancellation idempotency key was reused with a different request. |
409 | CONNECT_ALLOWANCE_STATUS_IDEMPOTENCY_CONFLICT | A pause or resume idempotency key was reused with a different request. |
409 | CONNECT_CREDIT_LEDGER_EXPORT_IDEMPOTENCY_CONFLICT | An export idempotency key was reused with different filters. |
409 | CONNECT_ALLOWANCE_STATUS_INVALID_STATE_TRANSITION | The requested pause or resume transition is not allowed. |
412 | CONNECT_ALLOWANCE_WRITE_PRECONDITION_FAILED | If-Match or If-None-Match does not match current state. |
412 | CONNECT_SCHEDULED_ALLOWANCE_WRITE_PRECONDITION_FAILED | The scheduled allowance create or replace precondition does not match current state. |
412 | CONNECT_SCHEDULED_ALLOWANCE_CANCEL_PRECONDITION_FAILED | The scheduled allowance version changed before cancellation. |
412 | CONNECT_ALLOWANCE_STATUS_PRECONDITION_FAILED | The current allowance version changed before pause or resume. |
429 | CONNECT_RATE_LIMIT_EXCEEDED | The workspace exceeded the endpoint's rate limit. Use Retry-After before retrying. |
502 | CONNECT_CREDIT_LEDGER_UNAVAILABLE | A credit dependency could not complete the request. |
504 | CONNECT_CREDIT_LEDGER_TIMEOUT | A credit dependency timed out. |
For a stale version, read the resource again, review its latest state, and retry with a new idempotency key and the new version only if the change is still intended.
Rate limits
Rate limits are applied per workspace. Endpoints in the same row share a rate-limit bucket.
| Endpoints | Limit |
|---|---|
| Read pool | 300 requests per minute |
| List allowances and read one company | 300 requests per minute |
| Create or replace current, create or replace scheduled, cancel, pause, and resume | 60 requests per minute |
| Submit bulk changes | 30 requests per minute |
| Read bulk job | 120 requests per minute |
| Read ledger | 120 requests per minute |
| Request export and read export status | 30 requests per minute |
When a limit is exceeded, the API returns 429 Too Many Requests and a Retry-After header.
Webhooks
Credit events and delivery behavior are documented separately in ISV Credit Webhooks.