Core Concepts
This page documents the shared concepts used across all Agile Receivables API endpoints: authentication, response envelopes, errors, pagination, and rate limits.
Authentication
All requests to the Agile Receivables external API must include an API key in the X-Api-Key header:
curl --request GET \
--url https://api.agilereceivables.com/api/v1/external/customers \
--header 'X-Api-Key: sk_live_xxxxxxxxxxxxxxxxxxxx'- Keys are managed from the Integrations / API Keys section of the dashboard.
- Supported scopes are:
accounts:read,accounts:writecustomers:read,customers:writecampaigns:read,campaigns:writedisputes:read,disputes:writeledgers:read,ledgers:writestatements:read,statements:writesync:write
- An API key with an empty scope list has full access (no restriction).
- Restriction applies only when scopes are explicitly set.
Only X-Api-Key is required. No additional tenant header is needed; workspace/company context is derived from the API key.
Response envelope
All endpoints return a consistent JSON envelope:
{
"success": true,
"status_code": 200,
"message": "...",
"data": { },
"meta": { }
}success: boolean flag indicating whether the operation completed successfully.status_code: HTTP-style numeric code mirrored in the HTTP status.message: human-readable description of the outcome.data: payload for the current resource (for example, a customer or account object, or a list).meta: metadata, including pagination information for list endpoints.
Errors
Auth failures return simple detail payloads:
{ "detail": "Invalid or expired API key" }- 401 Unauthorized: missing, invalid, revoked, or expired API key.
{ "detail": "API key lacks required scope: <scope_name>" }- 403 Forbidden: API key does not include the required scope.
General API failures use a consistent error envelope:
{
"success": false,
"status_code": 400,
"message": "User-facing error message",
"errors": [
{
"field": "amount",
"error": "Payment amount cannot exceed outstanding amount"
}
],
"meta": {}
}Common HTTP status codes:
| Status | Name | Description |
|---|---|---|
| 400 | Bad Request | Validation error in the request body or parameters. |
| 401 | Unauthorized | Missing, invalid, revoked, or expired API key. |
| 403 | Forbidden | API key lacks the required scope. |
| 404 | Not Found | Resource does not exist for your tenant. |
| 422 | Unprocessable | JSON is valid but fields fail validation. |
| 429 | Too Many | Rate limit exceeded for this API key. |
| 500 | Server Error | Unexpected internal server error. |
errors uses field and error. There is no machine-readable error_code field.
Pagination
Paginated endpoints (such as list customers, list accounts, and list disputes) use a standard meta object:
{
"meta": {
"page": 1,
"per_page": 20,
"total": 150,
"total_pages": 8
}
}total: total number of records matching the current filters. Always present (defaults to0when empty).total_pages: total number of pages for the currentper_page.page: 1-based page index.per_page: number of records returned per page.- Pagination is page-based only (cursor pagination is not supported).
Typical query parameters for list endpoints:
page: page number (default: 1, min: 1).per_page: results per page (default: 20, min: 1, max: 100).
Some endpoints define additional filters, for example:
- Customers:
search,tags. - Accounts:
search,status,customer_id,overdue_only,amount_min,amount_max. - Campaigns:
search,status,type. - Disputes:
status,customer_id. - Ledgers:
customer_id,customer_external_id,from_date,to_date,transaction_type(per-page max 200). - Statements:
customer_id,customer_external_id,status,from_date,to_date.
Rate limiting
For platform middleware overall, rate limiting is configured by RATE_LIMIT_PER_MINUTE over a
60-second window and is applied per user ID or IP.
Headers:
X-RateLimit-LimitX-RateLimit-Window
For the external API (/api/v1/external/*), this middleware is currently excluded.
There is no separate test-vs-live key rate limit behavior, and no per-tenant/per-key limit.
Sandbox/live behavior is controlled by record-level is_sandbox flags, not by API key type.
Sandbox accounts cannot be enrolled in campaigns. Sandbox keys are also blocked from enrolling
statements into Statement-Acknowledgement campaigns — these endpoints return 400 for
sk_test_* keys. The sandbox data-purge endpoint (/sandbox/data) clears ledger entries
alongside customers and invoices.
Typical integration flow
The following high-level flow is common when integrating with Agile Receivables:
-
Create or sync customers
Use the Customers endpoints to create or update customer records. Ensure that each customer has at least one contact point (emailorphone) and enough identifying information for your agents. -
Create or sync accounts
Use the Accounts endpoints to create invoices with amounts, currency, issue dates, and due dates. Optional fields (such asexternal_invoice_id,payment_terms, ordescription) can help with reporting. -
Assign accounts to campaigns
Use the Campaigns endpoints to place accounts into one or more collection campaigns, individually or in bulk. -
Record payments and reconcile
Use the Payments endpoints to record payments and track outstanding balances, then use your own systems to reconcile these with bank or processor statements.
Ledger-driven integrations (Sikka-style)
When the source system pushes raw transaction-level data instead of pre-built invoices, the flow is:
- Push customers with the Customers endpoints.
- Push ledger entries with the Ledgers endpoints (
POST /ledgers/bulk). The server upserts each entry and immediately runs the statement aggregator — one monthly statement per(customer, calendar month)is created or updated, with payments allocated across statements oldest-first. - Read back statements with the Statements endpoints to confirm what was materialized.
- Enroll statements into a Statement-Acknowledgement campaign with
POST /statements/{id}/campaign(or the bulk variant). SA campaigns must be created in-app.
The combined-sync endpoint POST /api/v1/external/sync also accepts ledger_entries in addition
to customers and invoices. The aggregator runs at the end of the pipeline whenever
ledger_entries are included.