API Documentation
The VerityPay API is a REST/JSON API designed for tenant-scoped automation. This section is the first stop for any integration.
Quick links
- Authentication — bearer tokens, abilities, tenant resolution.
- Endpoints — full reference of available endpoints.
- Webhooks — outbound event deliveries: envelope, signature verification, retries.
- Sandbox → Production — how environments work and the go-live migration checklist.
- Postman & MCP — import-ready collection and AI-agent integration.
Conventions at a glance
| Property | Value |
|---|---|
| Base URL | https://app.veritypay.com/api/v1 |
| Transport | HTTPS only (TLS 1.2+) |
| Authentication | Bearer token (Sanctum personal access token) |
| Tenant resolution | Encoded in token via central token_routes |
| Default content type | application/json |
| API version | v1 (in path) |
| Idempotency | Idempotency-Key header on writes (24h) |
| Request correlation | X-Request-Id header (set or echoed back) |
| Error envelope | { "error": { "code", "message" } } |
Versioning
Every endpoint lives under /api/v1/.... Breaking changes ship as a new major version (/api/v2/...); both versions run side by side during the deprecation window. Non-breaking additions land in v1.
A change is breaking if it removes a field, narrows a field's accepted values, or changes a field's type. Adding new optional request fields, new optional response fields, new endpoints, or new abilities is not breaking.
Request format
POST /api/v1/contacts HTTP/1.1
Host: app.veritypay.com
Authorization: Bearer vp_pat_••••••••••••••••••••
Accept: application/json
Content-Type: application/json
Idempotency-Key: 4f4f8b5e-4c5e-4d3a-8e9c-b3a3f2e1c0d5
{
"first_name": "Jane",
"last_name": "Doe",
"phone": "+15551234567"
}
Response format
Successful responses return JSON resources. Collection endpoints follow Laravel API Resources pagination:
{
"data": [ /* ... */ ],
"links": { "first": "...", "last": "...", "prev": null, "next": "..." },
"meta": { "current_page": 1, "per_page": 25, "total": 142 }
}
Error envelope
All /api/v1/* errors return a stable JSON shape so you can switch on error.code:
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid.",
"details": { "phone": ["The phone field is required."] },
"request_id": "01HZK8XZTQ2C5J7YTX3J7Z3VYW"
}
}
| HTTP | error.code |
When |
|---|---|---|
| 400 | bad_request |
Malformed request body (e.g., invalid JSON). |
| 401 | unauthenticated |
Missing / invalid bearer token. |
| 401 | invalid_signature |
Signed-URL signature missing or invalid. |
| 403 | forbidden |
Token lacks the required ability. |
| 404 | resource_not_found |
Object does not exist or is not in this tenant. |
| 404 | route_not_found |
Endpoint does not exist. |
| 405 | method_not_allowed |
HTTP method not allowed on this endpoint. |
| 409 | idempotency_conflict |
Same Idempotency-Key reused with a different body. |
| 413 | batch_payload_too_large |
Batch ingest body exceeds the 5 MB payload cap; split the batch. |
| 419 | csrf_token_mismatch |
(Web only — never on JSON API.) |
| 422 | validation_failed |
Request body validation failed; see details. |
| 422 | invalid_transition |
State machine refused the transition (e.g. cancel after paid). |
| 429 | rate_limited |
Throttled; respect Retry-After. |
| 429 | batch_concurrency_limit_reached |
Tenant already has the maximum batches in flight; retry after Retry-After. |
| 5xx | server_error |
Unhandled server-side error; report with request_id. |
The envelope always carries error.request_id when one was assigned. Always log this — support cannot triage without it.
Rate limits
| Surface | Limit |
|---|---|
| Authenticated API | 60 requests / minute / token |
| Batch ingest submissions | 10 requests / minute / tenant by default (plus max 2 batches in flight; both per-tenant configurable — see Batch ingest) |
| Unauthenticated API | 30 requests / minute / IP |
| Webhook receivers | 120 requests / minute / IP |
Rate-limit headers (X-RateLimit-Limit, X-RateLimit-Remaining) and a Retry-After header on 429s let you back off cleanly. Build retry-with-jitter into any production client.
Idempotency
Every writing call (POST/PUT/PATCH) accepts an Idempotency-Key header. Send a fresh, opaque, ≤128-char key per logical operation:
Idempotency-Key: 4f4f8b5e-4c5e-4d3a-8e9c-b3a3f2e1c0d5
VerityPay caches the response for 24 hours. Replays with the same key + same body return the original response. Replays with the same key + different body return 409 idempotency_conflict. Use UUID v4s — never derive the key from request contents.
Request correlation
Every request gets an X-Request-Id header. If your client supplies one (≤64 chars, ULID or UUID), the server echoes it back; otherwise the server assigns one. The same value appears on every error envelope (error.request_id), every audit log row, and every internal log line — making cross-system debugging trivial.
Best practices for integration teams
A short opinionated checklist that has saved teams real engineering hours:
- Generate one API token per service, not one per developer. Document which token belongs to which workload in your secrets manager.
- Grant the minimum abilities the workload needs (see Authentication). It's cheaper to add an ability later than to debug a leaked over-privileged token.
- Always send
Idempotency-Keyon writes. Networks lie. Retries are inevitable. Without a key you will eventually create duplicate payables. - Always log
request_idalongside your own correlation IDs. When something breaks, support can pivot from your trace ID directly to ours. - Always handle 429 with exponential backoff + jitter. Spinning on a limited endpoint just delays recovery for everyone, including you.
- Pin the API version in code. Don't dynamically resolve
/api/v1vs/api/v2; make the upgrade an explicit, reviewed deploy. - Validate webhook signatures before trusting payloads. VerityPay's outbound webhooks are HMAC-signed — verify every delivery per the Webhooks guide before acting on it.
- Treat phone numbers as the canonical identity in this platform. Always store them E.164-formatted on your side and let VerityPay normalise on ingest.
- Soft-delete first, hard-delete only on tenant offboarding. The API supports
restorefor transient mistakes — use it instead of recreating. - Smoke-test in your tenant before promoting. Each tenant has its own dataset; a token tested in staging will not work in production.