Complyance Logo

ISV credit webhooks

⚠️ Internal sandbox preview — not approved for partner or production use. Credit reads and writes do not yet share one environment model. Do not integrate against production credit data until Complyance confirms the environment contract.

Credit activity is published as webhook events so your system can react without polling. Register a webhook subscription for your workspace with a webhookUrl, the list of events to receive, and an optional secretKey for signing. Owners, Admins, and Partners can manage credit webhook subscriptions. Subscriptions are matched per workspace, per event type, and per data environment.

Supported events

EventTrigger
isv.credits.allowance.changedA current or scheduled allowance was created, replaced, cancelled, activated, or superseded.
isv.credits.allowance.pausedA current allowance was paused.
isv.credits.allowance.resumedA paused allowance was resumed.
isv.credits.allowance.expiredAn allowance period ended.
isv.credits.company.thresholdA company's committed usage (finalized plus reserved) crossed 80% of its current allowance.
isv.credits.company.exhaustedA company's available allowance reached zero.
isv.credits.pool.lowThe available ISV pool crossed a low threshold. Emitted once per crossed threshold at 20% and at 10% remaining.
isv.credits.pool.exhaustedThe available ISV pool reached zero.
isv.credits.consumption.reversedA finalized consumption was reversed.
isv.credits.bulk.completedA bulk allowance job finished, with or without row errors.

Threshold behaviour

  • company.threshold fires when committed usage crosses from below 80% to 80% or more of the current allowance. The payload carries thresholdPercent: 80.
  • company.exhausted fires when the company's available allowance goes from above zero to exactly zero, and takes precedence over the threshold event.
  • pool.low fires once per crossed threshold — at 20% and at 10% of pool credits remaining. Each threshold has its own event identity, so one consumption that crosses both emits two events, each carrying its thresholdPercent.
  • pool.exhausted fires when the pool reaches exactly zero and takes precedence over pool.low.

Delivery format

Events are delivered as an HTTP POST to your subscription URL:

HeaderDescription
Content-Typeapplication/json
X-Webhook-IDThe unique event ID. Use it for deduplication.
X-Webhook-EventThe event type, for example isv.credits.pool.low.
X-Webhook-TimestampDelivery timestamp in ISO 8601 format.
X-Webhook-SignaturePresent when the subscription has a secretKey: hex HMAC (SHA-256 by default, SHA-512 supported) of the raw request body. Verify it before trusting the payload.

Request body envelope:

{
  "eventId": "isv-credit:0f6c…",
  "data": {
    "id": "isv-credit:0f6c…",
    "type": "isv.credits.allowance.changed",
    "timestamp": "2026-08-12T10:00:00.000Z",
    "data": {}
  }
}

The inner data object is the event payload. Every credit payload carries schemaVersion (currently "1.0").

Event payloads

Allowance lifecycle events (allowance.changed, allowance.paused, allowance.resumed, allowance.expired):

{
  "schemaVersion": "1.0",
  "companyId": "{COMPANY_ID}",
  "action": "current.replaced",
  "allowance": {
    "target": "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": 4,
    "finalizedUsage": 250,
    "reserved": 20
  },
  "reason": "Plan upgrade",
  "bulkJobId": "{JOB_ID}"
}

action is one of current.created, current.replaced, current.paused, current.resumed, current.expired, scheduled.created, scheduled.replaced, scheduled.cancelled, scheduled.activated, or scheduled.expired. bulkJobId is present only when the change came from a bulk job.

Company capacity events (company.threshold, company.exhausted):

{
  "schemaVersion": "1.0",
  "companyId": "{COMPANY_ID}",
  "allowancePeriodId": "period_01",
  "reservationId": "res_01",
  "limit": 1000,
  "finalizedUsage": 700,
  "reserved": 100,
  "committedUsage": 800,
  "available": 200,
  "percentageUsed": 80,
  "thresholdPercent": 80,
  "amount": 10
}

thresholdPercent is present on company.threshold only.

Pool capacity events (pool.low, pool.exhausted):

{
  "schemaVersion": "1.0",
  "triggeringCompanyId": "{COMPANY_ID}",
  "reservationId": "res_01",
  "totalCredits": 10000,
  "availableCredits": 1900,
  "percentageAvailable": 19,
  "thresholdPercent": 20,
  "amount": 25
}

thresholdPercent is present on pool.low only.

consumption.reversed:

{
  "schemaVersion": "1.0",
  "companyId": "{COMPANY_ID}",
  "reservationId": "res_01",
  "transactionId": "txn_02",
  "originalTransactionId": "txn_01",
  "allowancePeriodId": "period_01",
  "amount": 1,
  "reason": "Document rejected",
  "restorePool": true,
  "countryCode": "AE",
  "documentType": "invoice",
  "balanceBefore": { "companyAvailable": 730, "poolAvailable": 6800 },
  "balanceAfter": { "companyAvailable": 731, "poolAvailable": 6801 }
}

bulk.completed:

{
  "schemaVersion": "1.0",
  "jobId": "{JOB_ID}",
  "status": "completed_with_errors",
  "totals": {
    "total": 2,
    "processed": 2,
    "succeeded": 1,
    "failed": 1
  },
  "completedAt": "2026-08-12T10:05:00.000Z"
}

Versioning

Event type names are stable identifiers. Payloads carry schemaVersion, currently "1.0". New optional fields may be added without a version change; breaking payload changes increment schemaVersion. Ignore unknown fields.

Deduplication

Delivery is at-least-once. The event ID is deterministic for the underlying occurrence, so an idempotent replay of the same write produces the same ID, and retries redeliver the same ID. Deduplicate on eventId (also sent as X-Webhook-ID). For pool.low, the crossed threshold is part of the event identity, so the 20% and 10% notifications are distinct events that do not deduplicate each other.

Environment

Each event is produced in the data environment of the operation that caused it and is delivered only to subscriptions for that environment. Until the credit API environment model is unified, treat webhook traffic with the same caution as the write APIs.

Retries and failure handling

Each subscription has independent delivery state. A delivery succeeds on any 2xx response. A 4xx response is treated as permanent and is not retried. 5xx responses, timeouts, and network failures are retried up to 5 attempts per subscription, with delays of 60 seconds, then 5 minutes, then 15 minutes between the remaining attempts. After the final attempt, the delivery is marked failed and is not redelivered.

Respond 2xx quickly and process asynchronously. Events can arrive out of order — the delivery pipeline does not guarantee ordering — so order by the payload timestamp (and allowance.version for allowance events) rather than arrival order.

Replay and reconciliation

There is no partner-facing replay API. If deliveries were missed, reconcile from the authoritative credit ledger using GET /v3/connect/credits/transactions or a ledger export. Event and delivery records are retained internally by Complyance for troubleshooting; contact support with the eventId when investigating a delivery.