Certification
Before you go live we check that your integration behaves. You can run the same checks yourself, as many times as you like, against the sandbox — nothing here is a surprise on the day.
pnpm certify -- --level 3 --base-url https://connect-staging.getcardy.net/api/v1 --key cardy_pk_test_… --phone 96599901001 --include-slow
Levels are cumulative: passing level 3 means levels 1 and 2 pass too. A partner who can redeem but cannot earn is not certified for anything.
How the results read
PASS |
Checked and correct. |
FAIL |
Checked and wrong. The output says what was expected and what came back. |
SKIP |
Not checked. Add --include-slow for the checks that wait on a clock. |
ATTEST |
Cannot be checked through an API — you confirm it, we take your word. |
A run only says CERTIFIED when nothing failed and nothing went unchecked.
"Nothing failed" is not the same as "everything passed", and the exit code
reflects that.
Level 1 — you can earn without paying twice
The floor. Six checks, all automated.
- An earn is accepted and reported back
- Re-sending the same
external_refdoes not award twice - A back-dated earn inside the offline window is accepted
- A back-dated earn beyond the window is refused
- A malformed body answers
VALIDATION_ERROR - A bad key answers
UNAUTHENTICATEDand nothing else
The back-dated pair is about tills that lose connectivity. Queue the sales, send
them later with occurred_at set to when they actually happened, and they post
with the right date. Beyond the window (24 hours by default) they are refused —
that is what stops a mis-configured till replaying last month into a period the
store has already closed its books on.
Level 2 — you can look a customer up and spend their balance
Four automated checks and two you attest to.
- Identify by phone returns the customer and their cards
- Every card says which operations it allows
- A redemption is applied against a real balance
- Over-spending answers
INSUFFICIENT_BALANCE - You attest: the till greys out operations the card does not allow
- You attest: the till retries
UPSTREAM_TIMEOUTwith the sameexternal_ref
The two attestations are properties of your screen and your retry loop, and no API call can observe either.
The second one is the one that matters. Retrying a timeout with a new receipt number is the only way to pay a customer twice, and nothing will tell you it happened — both requests succeed. If you take one thing from this page, take that.
Level 3 — enrollment, refunds, held balances, reconciliation
Six automated checks and one you attest to.
- Enrollment is accepted and a code is sent
- A wrong code is refused with a reason
- You attest: enrollment completes with the delivered code
- A sale can be reversed by the receipt number it was sent with
- A sale can be found again by the receipt number the till sent
- Two simultaneous holds on the same balance — exactly one wins
- Committing an expired hold answers
RESERVATION_EXPIRED(needs--include-slow)
Completing an enrollment is an attestation today rather than a check, because the sandbox sends its verification codes somewhere only we can read. You demonstrate that half; we are working on making it self-service.
The two-holds check is worth understanding. If two terminals scan the same customer at the same moment, exactly one hold succeeds. Both succeeding would let one balance be spent twice.
What "certified" gets you
A live key. Until then your test key reaches the sandbox only, which is the point — there is no configuration mistake you can make that spends a real customer's balance before you have shown the integration works.