Getting started
Verifying the payer
Every deposit request names who may pay and what they must prove first. You assert; Payday confirms; and whatever the policy, the payer signs once from the wallet they will pay from before an address exists.
Verification answers a question a bank transfer never could: was the money sent by the person you were expecting, and did they see what they were paying for? Payday splits that into two parts. The payer policy decides what a person proves before the request's content is shown to them. The wallet step then ties that person to the wallet the funds will come from.
The three policies#
{ "mode": "permissionless" }
{ "mode": "verified_email", "expected_email": "alice@customer.example" }
{ "mode": "merchant_session", "payer_reference": "user_123" }Permissionless
open linkAnyone holding the link sees the request and can pay it. Nothing is proved beyond the wallet step.
Use it for a one-off invoice you email yourself, or when the link is handed over in a channel you already trust.
Verified email
emailed codeYou name the mailbox. The checkout shows only your name and the heading until the payer types a code sent to that mailbox. Payday sends the code; the payer never types an address.
Use it when a request must only be seen and paid by a specific person you know by email.
Merchant session
your sign-inYour application has already signed the user in. It creates the request naming that user by your own id, receives a single-use client secret, and opens the checkout with it. The page opens unlocked; the payer types nothing.
Use it for an exchange, a fund, or any product crediting a logged-in customer.
The merchant asserts, Payday confirms. Payday tells you whether the check passed and when, never the payer's own data. The full policy, including the expected email or the payer reference, is returned only to you; the payer page shows a masked hint such as a****@c***.example for verified email, and nothing at all for a merchant session.
What the payer sees, and when#
A gated request discloses progressively. Until the policy is satisfied, only the issuer name and heading are shown; until the wallet is bound, there is no address to show.
| Shown | Always | After verification | After the wallet step |
|---|---|---|---|
| Issuer name and heading | |||
| Status, deadline, whether it is payable | |||
| Amount, remaining, chain, token | — | ||
| Payer name, notes, reference, PDF | — | ||
| One-time address, QR, wallet button | — | — |
- Your payout address
- The recovery address
- Metadata
- The customer record
- The expected email (only a masked hint)
- The payer reference
Verified email, step by step#
- One code per request per minute, whoever asks. A wrong, spent, or expired code fails.
- The code is delivered by Payday's email provider from a Payday domain and names no deposit data.
- Funds sent before verification completes still count, but the request is flagged likely unsolicited, because the attested wallet did not pay them.
- After settlement, the same exchange re-proves the mailbox to reopen the receipt; it never makes the request payable again.
Merchant session, step by step#
This is the mode for applications with their own sign-in. Your server creates the request, receives a client_secret once, and sends the signed-in user to the checkout with that secret in the URL fragment. Exchanging the secret is the verification.
- The secret is returned exactly once, on the
201. It is never on a replay or a later read; Payday stores only its hash. If your server loses it, mint another withPOST /v1/deposit-requests/{id}/client-secret. - A secret is spent by its first exchange. The same link pasted into a second window is refused, and the page says so. When the user comes back later, mint a fresh secret and redirect again.
- Every webhook for the request carries your
payer_reference, so a handler credits the right ledger with no lookup. - What Payday attests is narrow and stated plainly: your server released this secret, and it was exchanged before this session saw the request. Who the payer is remains your assertion.
The wallet step#
Whichever policy a request has, the payer signs one message before the address exists. It is an EIP-712 typed-data signature, the kind every wallet displays in full before signing: a plain statement, the request's attribution hash, the wallet address, a one-time nonce, and an expiry. No transaction, no gas, no approval.
| Field | What it is |
|---|---|
| statement | A sentence the wallet shows: what the payer is agreeing to. |
| attributionHash | The hash of the issued document, so the signature belongs to this request only. |
| wallet | The address that will pay. The signature must recover to it. |
| nonce | Minted for this session once the policy is satisfied, valid for ten minutes. It is what ties the person who passed the checks to the wallet. |
| expiresAt | When the challenge is void. |
Once accepted, the salt, the recovery term (the wallet), and the address are written together, once. A deposit_request.ready webhook reports it, and the checkout shows the address, the QR, and the pay button. The pay button refuses to send from any wallet but the attested one.
Reading verification state#
Every request reports verification separately from its status:
verification_completed_at: when the policy was satisfied;nulluntil then, and alwaysnullfor permissionless requests;likely_unsolicited_at: when finalized funds first arrived from a wallet other than the attested one;payer_walletandwallet_bound_at: the attested wallet and when it was bound;GET /v1/deposit-requests/{id}/verification: each fact on its own (email, merchant session, wallet) and every attempt the payer made, with its outcome and time. Never the code, the secret, or the session.
The list route filters on verification=not_required|pending|verified|likely_unsolicited alongside status, and webhooks raise verification.approved, deposit_request.ready, and deposit_request.likely_unsolicited the moment each fact is first recorded.