Overview
PayCA dispatches a webhook every time a cardholder's status changes, so your system can react in real time without polling the API.
Registering a webhook
curl -s -X POST "$PAYCA_BASE_URL/v1/webhooks" \
-H 'Content-Type: application/json' \
-H "x-client-id: $PAYCA_CLIENT_ID" \
-H "x-client-secret: $PAYCA_CLIENT_SECRET" \
-d '{
"url": "https://your-domain.com/webhooks/cardholder",
"type": "cardholder"
}'
url— HTTPS endpoint on your side that will receive the webhooks.type— webhook category. Use"cardholder"for cardholder events.
Payload
Every status change triggers a POST to your URL with the following JSON body:
{
"cardholderId": "1ff17026-f9d7-4870-8d48-daee288edd0a",
"status": "pass_audit",
"previousStatus": "under_review",
"description": "",
"timestamp": "2026-03-25T12:45:00Z"
}
Fields
| Field | Type | Description |
|---|---|---|
cardholderId |
string (UUID) | Cardholder identifier. |
status |
string | New cardholder status. |
previousStatus |
string | Previous cardholder status. |
description |
string | Rejection reason (set on rejected_by_admin and reject). |
timestamp |
string (ISO 8601) | Time of the status change. |
Statuses
A cardholder can be in any of the following states:
| Status | Meaning |
|---|---|
pending_review |
Created, waiting for the first PayCA admin review. |
rejected_by_admin |
Rejected by a PayCA admin (see description). |
wait_audit |
Submitted to the provider; waiting for a provider response. |
under_review |
Provider has started its review. |
pending_approval |
Provider approved; waiting for a second confirmation by a PayCA admin. |
pass_audit |
Fully approved — ready for card issuance. |
reject |
Rejected by the provider (see description and statusFlowLocation). |
submission_failed |
Could not be sent to the provider (e.g. API error). An admin can retry. |
Transitions and webhooks
A cardholder is created in pending_review (no webhook). Subsequent transitions emit a webhook to your URL.
pending_review ─┬─→ rejected_by_admin
└─→ (admin approval — submit to provider)
│
├─→ wait_audit / under_review
│ │
│ ├─→ pending_approval ─┬─→ pass_audit
│ │ ├─→ rejected_by_admin
│ │ └─→ reject
│ └─→ reject
│
└─→ submission_failed
│
└─[retry]─→ wait_audit → ...
| Transition | When it fires |
|---|---|
pending_review → rejected_by_admin |
A PayCA admin rejected the cardholder. |
pending_review → wait_audit / under_review / pending_approval |
Admin approved and we submitted to the provider; the resulting status depends on the provider's response. |
pending_review → submission_failed |
Submission to the provider failed; reason is in description. |
wait_audit → under_review |
The provider started a manual review. |
wait_audit / under_review → pending_approval |
The provider approved; awaiting the second PayCA confirmation. |
wait_audit / under_review → reject |
The provider rejected (reason in description). |
pending_approval → pass_audit |
A PayCA admin gave the final approval — ready for card issuance. |
pending_approval → rejected_by_admin |
A PayCA admin declined at the final step. |
submission_failed → wait_audit |
An admin retried the submission. |
previousStatusalways reflects the state immediately before this webhook. Branch on the(status, previousStatus)pair rather than assume a fixed sequence — the path may vary by provider.
Webhook examples
PayCA admin approved
{
"cardholderId": "1ff17026-f9d7-4870-8d48-daee288edd0a",
"status": "wait_audit",
"previousStatus": "pending_review",
"description": "",
"timestamp": "2026-03-25T10:35:00Z"
}
Provider audit passed
{
"cardholderId": "1ff17026-f9d7-4870-8d48-daee288edd0a",
"status": "pass_audit",
"previousStatus": "under_review",
"description": "",
"timestamp": "2026-03-25T12:45:00Z"
}
Action: the cardholder is approved — issue a card via POST /v1/cards.
Provider rejected
{
"cardholderId": "1ff17026-f9d7-4870-8d48-daee288edd0a",
"status": "reject",
"previousStatus": "under_review",
"description": "Document verification failed: ID photo is unclear",
"timestamp": "2026-03-25T14:00:00Z"
}
PayCA admin rejected
{
"cardholderId": "1ff17026-f9d7-4870-8d48-daee288edd0a",
"status": "rejected_by_admin",
"previousStatus": "pending_review",
"description": "Incomplete personal data",
"timestamp": "2026-03-25T11:00:00Z"
}
Handling guidance
- Reply with HTTP 200 — PayCA expects a success response. Failures trigger redelivery.
- Be idempotent — the same webhook may be redelivered. Deduplicate on
cardholderId+status+timestamp. - React to
pass_audit— it signals that you can now issue a card for this cardholder. - Log
rejectandrejected_by_admin—descriptioncarries the rejection reason you can surface to the end user.
Authentication
Every API request requires two headers:
x-client-id: <your-client-id>
x-client-secret: <your-client-secret>
Base URL: https://api.actisas.ee