Auto-accept incoming payments
Make incoming payments spendable without a manual accept. Arm a wallet so a background worker sweeps its incoming (one asset, a set, or all — present and future), or call acceptAll on demand. Covers the mint-key → setAcceptAutomation flow, status/revoke, group-wallet delegation, and the default-on UX pattern.
In YieldFabric's confidential model, money sent to a wallet doesn't become spendable on arrival — it lands as a pending (locked-in) payment that the recipient must accept. Until accepted it can't fund a send, settle a swap, or be disbursed.
Auto-accept removes that manual step: you arm a wallet once, and a background worker sweeps its incoming payments into spendable balance on a schedule — for one asset, a set of assets, or all assets (present and future). If you'd rather not run the background worker, you can accept on demand with a single call.
This guide is for developers building their own UX on top of the YieldFabric APIs. It covers both paths and the exact operations to call.
- Auth + GraphQL setup: Building with YieldFabric
- Conceptual background on locked-in vs spendable: Balances
The two paths
acceptAll (on demand)setAcceptAutomation onceacceptAll (on demand)acceptAll whenever you wantacceptAll (on demand)acceptAll (on demand)Both ultimately call the same accept logic. Auto-accept is just acceptAll on
a timer, with a sealed credential so the worker can act as the wallet unattended.
Path A — accept on demand (acceptAll)
One mutation. Accepts every pending payable for a wallet + denomination that's unlocked (past its release time) and addressed to that wallet.
# POST pay.test.yieldfabric.com/graphql (Authorization: Bearer <user JWT>)
mutation AcceptAll($input: AcceptAllInput!) {
acceptAll(input: $input) {
success
message
totalPayments
acceptedCount
failedCount
}
}
// variables — accept ALL assets for the wallet
{ "input": { "walletId": "WLT-0x…", "denomination": "" } }
// variables — accept just one asset
{ "input": { "walletId": "WLT-0x…", "denomination": "aud-token-asset" } }
AcceptAllInput fields:
denominationString!"" = all assets (wildcard).walletIdStringobligorStringidempotencyKeyStringThe caller must be able to act for walletId (it's one of your own wallets, or
a group you control — see Group wallets). acceptAll is safe
to call repeatedly: once a payment is accepted it's no longer pending, so a
re-run accepts nothing.
Path B — arm auto-accept (background)
Arming is a two-step flow: mint a revocable API key, then register it against the wallet. The worker uses that key to act as the wallet unattended.
Step 1 — mint a key
# POST auth.yieldfabric.com/auth/api-key/generate (Authorization: Bearer <user JWT>)
curl -X POST https://auth.yieldfabric.com/auth/api-key/generate \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "service_name": "auto-accept-WLT-0x…", "description": "Auto-accept incoming" }'
# → { "id": "<key-uuid>", "api_key": "yf_api_…", "service_name": "…", "created_at": "…" }
The plaintext api_key is returned once — you pass it straight into Step 2,
which seals it server-side. Keep the id: it's what later revokes the key.
Step 2 — register it against the wallet
# POST pay.test.yieldfabric.com/graphql (Authorization: Bearer <user JWT>)
mutation SetAcceptAutomation($input: SetAcceptAutomationInput!) {
setAcceptAutomation(input: $input) {
active
walletId
entityId
groupId
keyLabel
denominations
createdAt
}
}
// variables — auto-accept ALL assets (recommended default)
{
"input": {
"walletId": "WLT-0x…",
"apiKey": "yf_api_…", // from Step 1
"keyId": "<key-uuid>", // from Step 1 — enables full revoke
"keyLabel": "…last4", // optional, for your UI
"denominations": [] // [] = all assets, present and future
}
}
// variables — auto-accept specific assets only
{
"input": {
"walletId": "WLT-0x…",
"apiKey": "yf_api_…",
"keyId": "<key-uuid>",
"denominations": ["aud-token-asset", "usd-token-asset"]
}
}
SetAcceptAutomationInput fields:
walletIdString!apiKeyString!yf_api_… from Step 1. Stored sealed; never returned.keyIdStringkeyLabelStringgroupIdStringwalletId is a group account (see below).denominations[String!]![] = all (present + future). A non-empty list limits to those asset ids.From this point the worker accepts the wallet's incoming automatically. There's nothing else to call.
Check status
# POST pay.test.yieldfabric.com/graphql
query AcceptAutomationStatus($walletId: String!) {
dealFlow {
acceptAutomationStatus(walletId: $walletId) {
active # true when armed
walletId
groupId
keyLabel
denominations # [] means "all"
createdAt
}
}
}
Returns null when the wallet has never been configured, an object with
active: true when armed, and active: false when it was armed then stopped.
(The mutations above are also under the dealFlow namespace — shown without it
for brevity; nest them the same way if your client requires it.)
Stop
# POST pay.test.yieldfabric.com/graphql
mutation RevokeAcceptAutomation($input: RevokeAcceptAutomationInput!) {
revokeAcceptAutomation(input: $input) {
active # false after revoke
walletId
}
}
{ "input": { "walletId": "WLT-0x…" } }
Stopping revokes the credential and kills the underlying yf_api_… key at
auth (using the keyId you stored at arm time) — so a leaked key is dead, not
merely unreferenced.
Denominations: all vs specific
denominations: [](all) is the recommended default. It's a true wildcard — the worker accepts every incoming asset, including assets that don't exist yet at arm time. Use this when you want "any money sent here just lands."denominations: ["asset-a", "asset-b"]limits auto-accept to those assets. Anything else stays pending until accepted some other way.
Internally, the "all" mode issues a single acceptAll with an empty
denomination per tick; a specific list issues one acceptAll per asset.
Group wallets
A wallet owned by a group (e.g. a shared property or treasury account)
needs delegation: the worker has to sign as the group, not as the arming user.
Pass groupId when arming:
{
"input": {
"walletId": "WLT-0x…group-account…",
"groupId": "<group-entity-id>",
"apiKey": "yf_api_…",
"keyId": "<key-uuid>",
"denominations": []
}
}
The arming user must be the group's owner — the worker mints a group delegation token each tick, and that mint is the authoritative ownership check. A non-owner's auto-accept simply never fires (and is logged), so there's no silent wrong-account behavior.
For a directly-owned (personal) wallet, omit groupId.
Making it default-on in your UX
Auto-accept is off until armed — there's no global flag, because each wallet needs its own sealed key minted in the user's session. To make it feel "on by default," arm lazily when the user first opens a wallet:
- Query
acceptAutomationStatus(walletId). - If it returns
null(never configured) → mint a key +setAcceptAutomationwithdenominations: []. - If it returns an object → do nothing. An
active: falserow means the user explicitly stopped it; don't re-arm it (don't fight an explicit choice).
The null check is the durable, server-side guard: once a wallet is configured
the row exists, so re-opening never re-arms. For group wallets, wait until you
know the wallet is a group (and have its groupId) before arming, so you arm
with delegation rather than as a plain wallet. Guard the in-flight window with
a per-wallet "already attempted this session" flag so a fast re-open can't
double-mint.
Trade-off: arming mints one yf_api_… key per wallet the first time it's
opened. Surface those keys somewhere the user can review and revoke them.
How the worker behaves
- Cadence. It scans armed wallets roughly every 20 seconds. Acceptance is eventually-consistent, not instantaneous — a payment that lands between ticks is swept on the next one.
- Idempotency. Each tick calls
acceptAll, which only touches pending payments. Already-accepted money is skipped, so re-running is always a no-op. There's no double-accept. - Unlocked only. Payments with a future release time aren't accepted until they unlock — auto-accept respects the schedule, it doesn't bypass it.
- Key lifecycle. The worker exchanges the sealed key for a short-lived token
each cycle (cached briefly). The key is the user's own, revocable at any time;
revoking it (via
revokeAcceptAutomation, or directly at auth) stops the sweeps immediately.
Security notes
- The credential is the user's own revocable API key, sealed at rest. The worker holds no standing secret of its own — only what the user armed.
- Acceptance signs as the wallet (or, for a group wallet, as the group via delegation). The worker can't accept for a wallet the user can't act for.
- Accepting incoming money has no downside — it's funds owed to the wallet — so auto-accept needs no approval step or amount bound. (Disbursing out of a wallet is a different, policy-gated flow.)
- Stopping is a true kill: it revokes the local credential and the key at auth.