{"openapi":"3.1.0","info":{"title":"Fee Relayer API","version":"1.0.0","summary":"Co-signing relay for Solana USDC payments.","description":"The apex feerelayer.net is the service website; a subdomain is a relay. A relay co-signs a transaction you built and signed yourself, covering the SOL gas, and takes a USDC service fee inside that same transaction. It is non-custodial: it never holds funds and never sees your keys. Human-readable reference at https://feerelayer.net/docs."},"servers":[{"url":"https://{relay}.feerelayer.net","description":"A relay. The subdomain label IS the relay name.","variables":{"relay":{"default":"free","description":"The relay name, as listed by GET /relays.json on the apex."}}},{"url":"https://feerelayer.net","description":"The service website. Never a relay."}],"paths":{"/info":{"servers":[{"url":"https://{relay}.feerelayer.net","description":"A relay. The subdomain label IS the relay name.","variables":{"relay":{"default":"free","description":"The relay name, as listed by GET /relays.json on the apex."}}}],"get":{"operationId":"getInfo","summary":"The relay's co-signer wallet and fee policy.","description":"Read this before building anything: wallet is the pubkey to place at account slot 0, and fees.payment.recipients are the destinations your fee transfers must hit.","responses":{"200":{"description":"The relay policy.","content":{"application/json":{"schema":{"type":"object","required":["version","name","wallet","fees","supported_actions"],"properties":{"version":{"type":"integer","const":2},"name":{"type":"string"},"wallet":{"type":"string","description":"Base58 co-signer pubkey. Account slot 0 of your transaction."},"fees":{"type":"object","required":["memo","payment"],"properties":{"memo":{"type":"number","description":"USDC per free-flow memo. 0 on every hosted relay."},"payment":{"type":"object","required":["type","rate","min","recipients"],"properties":{"type":{"type":"string","const":"percent"},"rate":{"type":"number","minimum":0,"maximum":0.02,"description":"Fraction, not percent. 0.01 means 1%."},"min":{"type":"number","minimum":0.002,"maximum":0.1,"description":"Floor in USDC."},"recipients":{"type":"array","minItems":1,"maxItems":2,"description":"cut0 = min(fee, max(minRaw0, floor(fee * bps0 / 10000))), cut1 = fee - cut0. minRaw0 is recipient 0 min_usdc converted to raw units (0.01 USDC is 10000). cut1 collapses to 0 whenever the total fee is at or below minRaw0, and the second transfer is then omitted from the transaction.","items":{"type":"object","required":["ata","bps"],"properties":{"ata":{"type":"string","description":"Base58 USDC token account to pay."},"bps":{"type":"integer","minimum":0,"maximum":10000},"min_usdc":{"type":"number","minimum":0}}}}}}}},"free_cancellation":{"type":"boolean","const":false},"supported_actions":{"type":"array","items":{"type":"string","enum":["memo","payment"]}},"turnstile_sitekey":{"type":["string","null"],"description":"Public sitekey for free flows. Never needed for payments."}}}}}},"400":{"description":"relay_policy_invalid: the stored record fails the caps re-check.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"404":{"description":"unknown_relay.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}}}}},"/relay":{"servers":[{"url":"https://{relay}.feerelayer.net","description":"A relay. The subdomain label IS the relay name.","variables":{"relay":{"default":"free","description":"The relay name, as listed by GET /relays.json on the apex."}}}],"post":{"operationId":"postRelay","summary":"Co-sign and submit a transaction.","description":"The relay verifies that account slot 0 is its own wallet, refuses any transaction that abuses that signature against its own accounts, validates the declared fee split, then signs and submits. X-Turnstile-Token applies to free flows only.","parameters":[{"name":"X-Turnstile-Token","in":"header","required":false,"description":"FREE flows (type: memo) only. A type: payment request needs no token: it skips Turnstile and the quota stack entirely, because the USDC fee is the rate limit.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tx","type"],"properties":{"tx":{"type":"string","minLength":1,"description":"Base64 of the serialized, partially signed transaction. Account slot 0 must be the relay wallet reported by GET /info."},"type":{"type":"string","enum":["payment","memo"],"description":"payment skips Turnstile and the quota stack entirely (the USDC fee is the rate limit). memo is a free flow and pays the full anti-spam tax."},"mode":{"type":"string","enum":["sign"],"description":"Optional. Omit it and the relay submits the co-signed transaction to its own RPC. With \"sign\" the relay validates and co-signs identically but does not submit: the response carries its slot-0 signature (also \"mode\": \"sign\") and the client broadcasts through any RPC. A response without \"mode\" means the relay predates the option and has already submitted."}}}}}},"responses":{"200":{"description":"Co-signed and submitted.","headers":{"x-relay-mode":{"description":"The name of the relay that handled the transaction.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["signature"],"properties":{"signature":{"type":"string","description":"Base58 transaction signature."}}}}}},"400":{"description":"Malformed request, fee-payer mismatch, a refused co-signature, or failed validation.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"403":{"description":"Turnstile rejected the token. Free flows only.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"404":{"description":"unknown_relay: no record under this subdomain name.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"410":{"description":"A retired type or memo kind.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"429":{"description":"Quota exceeded, per IP or service wide. Free flows only.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"500":{"description":"The relay is misconfigured, or validation threw.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"502":{"description":"Submission to the Solana network failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}}}}},"/simulate":{"servers":[{"url":"https://{relay}.feerelayer.net","description":"A relay. The subdomain label IS the relay name.","variables":{"relay":{"default":"free","description":"The relay name, as listed by GET /relays.json on the apex."}}}],"post":{"operationId":"postSimulate","summary":"Dry run. Same checks as /relay, no signature, no submission.","description":"Takes the same body as POST /relay and runs the same resolution, parse, co-sign safety guard and validators, then stops and reports. Nothing is signed, nothing reaches the network, no quota is consumed and nothing is written. ALWAYS answers 200, including on a refusal: read ok for the verdict, reason for the string POST /relay would have returned, and status for the status it would have used. Two gaps are reported in notes rather than hidden: the protection stack is not run at all (so a memo simulation says nothing about Turnstile or quotas), and no live SOL price is fetched (so a thin ATA rent reimbursement can pass here and be refused there).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tx","type"],"properties":{"tx":{"type":"string","minLength":1,"description":"Base64 of the serialized, partially signed transaction. Account slot 0 must be the relay wallet reported by GET /info."},"type":{"type":"string","enum":["payment","memo"],"description":"payment skips Turnstile and the quota stack entirely (the USDC fee is the rate limit). memo is a free flow and pays the full anti-spam tax."},"mode":{"type":"string","enum":["sign"],"description":"Optional. Omit it and the relay submits the co-signed transaction to its own RPC. With \"sign\" the relay validates and co-signs identically but does not submit: the response carries its slot-0 signature (also \"mode\": \"sign\") and the client broadcasts through any RPC. A response without \"mode\" means the relay predates the option and has already submitted."}}}}}},"responses":{"200":{"description":"The report was produced. This is NOT a statement that the transaction is valid: read ok for that.","content":{"application/json":{"schema":{"type":"object","required":["ok","reason","status","relay","type","feeRequiredRaw","feeRequiredUsdc","feePaidRaw","feePaidUsdc","cuts","recipient","amountRaw","amountUsdc","wouldCosign","simulated","notes"],"properties":{"ok":{"type":"boolean","description":"Whether POST /relay would have accepted this transaction."},"reason":{"type":["string","null"],"description":"null when ok, otherwise the exact string POST /relay would have returned in its error field."},"status":{"type":"integer","description":"200 when ok, otherwise the status POST /relay would have used."},"relay":{"type":["string","null"],"description":"The relay resolved from the host."},"type":{"type":["string","null"],"description":"The flow type read from the body."},"feeRequiredRaw":{"type":["integer","null"],"description":"Total fee owed, raw USDC units."},"feeRequiredUsdc":{"type":["number","null"]},"feePaidRaw":{"type":["integer","null"],"description":"What the transaction actually sends to the fee recipients, raw units."},"feePaidUsdc":{"type":["number","null"]},"cuts":{"type":"array","description":"One entry per declared recipient. A recipient owed 0 is covered with no transfer at all, which is the cut1 collapse case.","items":{"type":"object","required":["index","ata","bps","minUsdc","requiredRaw","requiredUsdc","paidRaw","paidUsdc","present","covered"],"properties":{"index":{"type":"integer"},"ata":{"type":"string"},"bps":{"type":"integer"},"minUsdc":{"type":["number","null"]},"requiredRaw":{"type":"integer"},"requiredUsdc":{"type":["number","null"]},"paidRaw":{"type":"integer"},"paidUsdc":{"type":["number","null"]},"present":{"type":"boolean","description":"Whether a transfer to this recipient exists."},"covered":{"type":"boolean","description":"paidRaw >= requiredRaw."}}}},"recipient":{"type":["string","null"],"description":"Base58 destination token account of the transfer identified as the payment."},"amountRaw":{"type":["integer","null"]},"amountUsdc":{"type":["number","null"]},"wouldCosign":{"type":"boolean","description":"Derived from ok. Never true on a refusal."},"simulated":{"type":"boolean","const":true},"notes":{"type":"array","items":{"type":"string"},"description":"Ways this dry run is more permissive than POST /relay: the protection stack is not run, and no live SOL price is fetched for the ATA rent reimbursement check."}}}}}}}}},"/create":{"servers":[{"url":"https://feerelayer.net","description":"The service website. Never a relay."}],"post":{"operationId":"postCreate","summary":"Create a relay. Apex only.","description":"Self-service, no account. The relay is live at https://{name}.feerelayer.net as soon as this returns. Nothing is written on any rejection. On a hosted relay with a non-zero rate the record is created with two recipients, 3000 bps to the infrastructure with a 0.01 USDC floor and 7000 bps to the operator.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","payout_wallet","rpc","rate","min"],"properties":{"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{1,30}$","description":"Becomes the subdomain, and is immutable. A short list is reserved: helius, alchemy, test, www, api, relay, info, create."},"payout_wallet":{"type":"string","description":"Your base58 Solana wallet, 32 bytes. Not a token account: the USDC account is discovered on chain. Fund it with a little USDC first."},"rpc":{"type":"string","enum":["helius","alchemy"]},"rate":{"type":"number","minimum":0,"maximum":0.02,"description":"Fraction, not percent. 0.015 is 1.5%."},"min":{"type":"number","minimum":0.002,"maximum":0.1},"turnstileToken":{"type":"string","description":"Required whenever the service has Turnstile configured."}}}}}},"responses":{"200":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","required":["name","endpoint","share_link"],"properties":{"name":{"type":"string"},"endpoint":{"type":"string"},"share_link":{"type":"string"}}}}}},"400":{"description":"invalid_json, pseudo_required, pseudo_format, pseudo_reserved, wallet_invalid, rpc_invalid, rate_invalid, min_fix_invalid, or ata_mismatch.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"403":{"description":"turnstile_missing_token, turnstile_siteverify_failed, or turnstile_siteverify_unreachable.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"405":{"description":"method_not_allowed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"409":{"description":"pseudo_taken. Names are never reassigned.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"422":{"description":"ata_not_ready: the payout wallet has no USDC account yet.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"500":{"description":"misconfigured.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}},"502":{"description":"ata_lookup_failed. Fails closed: nothing is written.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"The literal failure string. See GET /docs for the full list."}}}}}}}}},"/relays.json":{"servers":[{"url":"https://feerelayer.net","description":"The service website. Never a relay."}],"get":{"operationId":"getRelays","summary":"The machine-readable relay list. Apex only.","description":"An order and nothing more. It publishes no transaction count, volume or popularity score of any kind, and payout accounts are dropped.","responses":{"200":{"description":"The list.","content":{"application/json":{"schema":{"type":"object","required":["v","relays"],"properties":{"v":{"type":"integer","const":1},"relays":{"type":"array","items":{"type":"object","required":["name","url","fees","house"],"properties":{"name":{"type":"string"},"url":{"type":"string"},"fees":{"type":"object"},"house":{"type":"boolean","description":"True for the relays this service itself operates."}}}}}}}}}}}}}}