Skip to content

App billing

/apps/billing/charges — any installation token, no scope. Billing reads nothing from the store, so there is no permission to grant; instead the merchant approves each charge in the CIQRA admin before anything is billed.

  1. POST /apps/billing/charges — you describe the charge; CIQRA stores it as pending and returns a confirmationUrl.
  2. Send the merchant to confirmationUrl. They see your app, the price, the trial and the cap, and approve or decline. The link is valid for 48 hours; after that the charge is expired.
  3. You learn the decision from the webhook below, or by reading GET /apps/billing/charges/{id}.

Nothing is billed before step 2. A recurring charge with a trial starts billing when the trial ends.

{ "kind": "recurring", "name": "Pro plan", "amount": 1900, "currency": "usd", "interval": "month", "trialDays": 14 }
Field Detail
kind one_time, recurring or usage_capped
name what the merchant sees, at most 120 characters
amount minor units (cents), positive, at most 10,000,000; must be 0 for usage_capped (it has no fixed amount)
currency usd, eur or try
interval recurring only: month or year
trialDays recurring only: 0 to 365
cappedAmount, terms usage_capped only: the ceiling the merchant approves (minor units) and what usage is billed for
returnUrl optional https URL the merchant returns to after deciding
test true for a test charge

201 returns the charge: id, kind, status, amounts, test, confirmationUrl (while pending), confirmationExpiresAt, approvedAt, billingOn (when a trial ends) and createdAt. A Stripe reference is never returned.

POST /apps/billing/charges/{id}/usage-records — Idempotency-Key required.

{ "description": "1,200 SMS sent", "amount": 2400 }

The charge must be an approved usage_capped charge. A record that would take the balance past the approved cappedAmount is refused with 422 cap_exceeded; ask for a new charge with a higher cap instead. Sending the same Idempotency-Key again returns the first result with Ciqra-Idempotent-Replay: true and records nothing.

DELETE /apps/billing/charges/{id} stops a pending, recurring or usage charge. Uninstalling your app cancels every charge it holds.

Subscribe your installation to app_subscriptions.update (recurring and usage charges) and app_purchases_one_time.update (one-time charges). They need no scope and are sent only to the installation that owns the charge — never to another app or to the merchant’s own subscriptions. Each is sent when a charge is created, approved, declined or cancelled:

{ "chargeId": "…", "status": "active", "kind": "recurring", "name": "Pro plan", "test": true }

Every refusal has the body { "error": "…", "code": "…", "fields": { … } } (fields only for validation).

Status code Meaning
400 idempotency_key_required a usage record was sent without Idempotency-Key
404 not_found no such charge for this installation
409 billing_not_live a non-test charge while paid charges are not live
409 currency_mismatch the charge’s currency is not the currency the merchant’s store is billed in
409 charge_not_active usage on a charge that is not approved and running
409 not_cancellable the charge already ended
409 idempotency_conflict the same Idempotency-Key is in flight or was used for a different record
422 validation_failed see fields for each refused field
422 cap_exceeded the usage record would pass the approved cap
422 not_a_usage_charge usage sent to a one-time or recurring charge
503 provider_unavailable the payment provider did not answer; nothing was recorded, retry with the same key