Idempotency and Outcome Recovery
Persist a client-generated event_id before submitting each payment operation that exposes one. Retain it with your order and the returned transaction_id. A new event ID represents a new operation; it is not a way to retry an uncertain payment.
Identifiers
| Identifier | Purpose |
|---|---|
event_id |
Identifies one operation, such as a sale, capture, or refund |
transaction_id |
Groups the original payment and its follow-up events |
Generate a UUID4 for event_id. Save it durably before the request leaves the device or backend. Keep the outcome and identifiers for reconciliation after app restarts. If you omit it, Koard can generate an ID, but a lost response then leaves your client without the lookup identifier.
SDK arguments vary by released platform version. For example, iOS capture accepts eventId, while Android 1.0.7 capture and tip adjustment do not expose that argument. Follow the platform-specific API instead of inventing an argument.
Duplicate Responses
A duplicate event ID is rejected with HTTP 400 and the stable error code duplicate_event_id. Look up that event's outcome; do not treat the duplicate error as a decline or generate a new ID to submit again. Branch on error, not human-readable details text.
Look Up an Event
Your backend can query:
curl 'https://api.uat.koard.com/v1/transactions/event/YOUR_PERSISTED_EVENT_ID' \
-H 'X-Koard-apikey: YOUR_API_KEY'
Requires payments:read and access to the account owning the event. Keep backend API keys out of shipped apps; expose an authenticated recovery endpoint through your backend if needed.
| Response | Action |
|---|---|
200 with a completed operation |
Reconcile the transaction status, amounts, and processor response with your order |
200 with pending |
Keep the operation unresolved and poll with bounded backoff |
200 with surcharge_pending |
Complete the supported cardholder confirmation flow; polling alone does not confirm the surcharge |
404 |
No visible committed event was found. The request may still be in flight, or your key may be querying the wrong account or environment |
| Authentication or permission failure | Correct access; do not infer a payment outcome from this response |
| Lookup timeout or server error | Keep the outcome unresolved and retry the lookup with backoff |
A 404 does not prove the original POST never reached the processor. Do not immediately resubmit, switch event IDs, or replay one-time EMV reader data or cryptograms based solely on a missing lookup result.
Recovery Sequence
- Save the operation's event ID, order, intended amount, merchant account, location, and environment before submitting.
- Submit once through the supported SDK or REST endpoint.
- Inspect a successful response's transaction status and amounts. Pending states, partial approvals, and recorded errors need their appropriate follow-up; HTTP 2xx alone is not enough.
- For a duplicate event, timeout, lost response, or server error, stop automatic payment retries and look up the saved event ID.
- Poll with bounded backoff, for example 1, 2, 5, 10, and 30 seconds. Reconcile against your signed webhook deliveries and processor reports when available.
- If the outcome remains unresolved, retain the identifiers and flag the order for investigation. Start a new reader session or operation only after resolving the previous outcome through the supported recovery process.
Duplicate detection is a safeguard, not a promise that concurrent or in-flight retries are safe. Avoid parallel submissions of the same operation.
Webhook Reconciliation
Verify webhook signatures and deduplicate by delivery ID. Transaction bodies include event_id and transaction_id; store them with the event outcome. A webhook mirror in your backend can support recovery when the app cannot reach Koard, but delayed or missing webhook delivery is not evidence of failure.
See Payment Lifecycle, Available Events, and Webhook Setup.

