{"openapi":"3.0.0","info":{"title":"IBEX API","version":"1.2.0-productionv2","description":"Advanced self-custody wallet API. Build gasless, passwordless dApps using FIDO2 passkeys + Safe Global wallet or EOA with account abstraction EIP-7702.\n\n📖 **[Full Documentation](/human)** · 🤖 **[AI Agents](/ai)** · 📋 **[skills.md](/skills.md)**\n\n> **Authentication:** All endpoints use the `/v1.2/` prefix. Start with `GET /v1.2/auth/sign-up` → `POST /v1.2/auth/sign-up`, then refresh sessions with `POST /v1.2/auth/refresh`.\n\n> **rpId (tenant):** Pass your registered rpId via query param (`?rpId=yourdomain.com`) or header (`X-RpId`). On development environments, `localhost` / `127.0.0.1` / `[::1]` work automatically. See `/docs/guides/integration/rpid` for details.\n\n> Multichain note: whenever a schema includes `blockchainId`, clients may pass it via the `X-Blockchain-Id` header (preferred) or as a query parameter. If omitted, a default from configuration is applied."},"components":{"securitySchemes":{"API_KEY":{"type":"apiKey","in":"header","name":"x-api-key"},"JWT":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"def-0":{"format":"evm-address","type":"string","title":"evm-address"},"def-1":{"format":"amount","type":"string","description":"A string representing a strictly positive decimal number (e.g., ETH) entered by a user, which will be converted to a wei value internally.","title":"amount"}}},"paths":{"/health":{"get":{"summary":"Health check","tags":["Infra"],"description":"Public endpoint to verify the API is responding (liveness probe).\n\n<span style=\"font-weight: 400; color: #000\">Expected response example:</span>\n<pre><code class=\"nohighlight\" style=\"color:#000\">{\"status\":\"ok\",\"timestamp\":\"2025-09-15T14:44:43.893Z\"}</code></pre>","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"timestamp":{"type":"string"}}}}}}}}},"/v1.2/auth/sign-up":{"get":{"summary":"Sign-up with passkey 1/2","tags":["Authentication"],"description":"Enhanced signup endpoint (v1.2) supporting 4 authentication methods.\n\n**rpId (tenant identification):**\nThe rpId (Relying Party Identifier) is a domain string that scopes users, wallets, and credentials to your application. Each rpId is an isolated namespace — users created under one rpId are invisible to another. It also serves as the WebAuthn rpId, so the browser enforces that it matches your page origin.\n\nYour rpId must be registered with IBEx. Pass it via:\n- Query parameter: `?rpId=yourdomain.com`\n- Header: `X-RpId: yourdomain.com`\n- Automatically via Origin header (browser requests)\n\nIf missing or unregistered, returns `400 Unknown domain/rpId`.\nFor local development, `localhost` is automatically recognized.\nSee the full [rpId guide](/docs/guides/integration/rpid) for production setup, WebAuthn constraints, and common errors.\n\n---\n\n**Query parameters reference:**\n\n| Parameter | Type | Required for |\n|-----------|------|-------------|\n| `wallet` | string | All modes (`passkeys` default, `kdf`, `email`, `7702`) |\n| `email` | string | `wallet=email` only |\n| `address` | string | `wallet=7702` only (0x-prefixed, 42 chars) |\n| `chainId` | number | `wallet=7702` (optional, defaults to project chain) |\n| `user.name` / `userName` | string | `wallet=passkeys` (optional, WebAuthn override) |\n| `user.displayname` / `userDisplayName` | string | `wallet=passkeys` (optional, WebAuthn override) |\n| `keyName` / `keyDisplayName` | string | `wallet=passkeys` (optional, display labels) |\n| `passkeys` | string | ⚠️ Deprecated — use `wallet` instead |\n| `flow` | string | ⚠️ Deprecated — use `wallet=kdf` instead |\n\n---\n\n### 1. Passkeys Safe wallet (`wallet=passkeys`, default)\n\n**What it means?**\nCreates a new user account backed by a WebAuthn passkey (biometric / security key). The browser generates a cryptographic key pair; the public key is registered as the signer for a new Safe Smart Contract Wallet (ERC-4337). This is the most secure mode — the private key never leaves the device.\n\n**Next step:** POST the `credential` returned by `navigator.credentials.create()` to `POST /sign-up`.\n\n---\n\n### 2. KDF Safe wallet (`wallet=kdf`)\n\n**What it means?**\nCreates a user account where the signer key is derived client-side from a user-chosen PIN/password using Argon2id. The server never sees the PIN — it only provides the salt and KDF parameters. A server-signed challenge prevents replay attacks. Ideal for devices without biometric/passkey support.\n\n**Next step:** Derive the key from PIN + salt, sign the canonical message, and call `POST /sign-up` with the signature proof.\n\n---\n\n### 3. Email Safe wallet (`wallet=email`, requires `email`)\n\n**What it means?**\nCreates an account where a one-shot email OTP (30 seconds) verifies the user's email address. The client generates a key pair locally and encrypts the private key with a user passphrase before sending it to the server as a backup. The server stores only the encrypted blob — it can never access the real key. Useful for email-first onboarding.\n\n**Next step:** Verify the OTP via `POST /email/recover`, then call `POST /sign-up` with the signature proof.\n\n---\n\n### 4. EOA + EIP-7702 (`wallet=7702`, requires `address`)\n\n**What it means?**\nRegisters an existing Externally Owned Account (EOA) and turns it into a Smart Account via EIP-7702 delegation to `SafeEIP7702Proxy`. The user signs a SIWE-like challenge with their wallet (MetaMask, WalletConnect, etc.). After sign-up, the backend creates a `PENDING` delegation record — the client must broadcast the EIP-7702 authorization transaction on-chain to activate Smart Account features (modules, batching, recovery).\n\n**Next step:** Sign `challenge` with the wallet and call `POST /sign-up` with `{ wallet: \"7702\", address, signature, nonce }`.","parameters":[{"schema":{"type":"string","enum":["passkeys","kdf","email","7702"]},"in":"query","name":"wallet","required":false,"description":"Authentication mode: passkeys (default), kdf, email, or 7702"},{"schema":{"type":"string","enum":["TRUE","FALSE"],"deprecated":true},"in":"query","name":"passkeys","required":false,"description":"[Deprecated] Use wallet=passkeys or wallet=kdf instead"},{"schema":{"type":"string","enum":["pin-kdf"],"deprecated":true},"in":"query","name":"flow","required":false,"description":"[Deprecated] Use wallet=kdf instead"},{"schema":{"type":"string"},"in":"query","name":"email","required":false,"description":"[Email mode only] Email address to receive OTP"},{"schema":{"type":"string"},"in":"query","name":"address","required":false,"description":"[7702 mode only] EOA address (0x-prefixed, 42 chars)"},{"schema":{"type":"number"},"in":"query","name":"chainId","required":false,"description":"[7702 mode only] Target blockchain chain ID"},{"schema":{"type":"string"},"in":"query","name":"user.name","required":false,"description":"[Passkeys mode] Override WebAuthn user.name"},{"schema":{"type":"string"},"in":"query","name":"user.displayname","required":false,"description":"[Passkeys mode] Override WebAuthn user.displayName"},{"schema":{"type":"string"},"in":"query","name":"userName","required":false,"description":"[Passkeys mode] Alias for user.name"},{"schema":{"type":"string"},"in":"query","name":"userDisplayName","required":false,"description":"[Passkeys mode] Alias for user.displayname"},{"schema":{"type":"string"},"in":"query","name":"keyName","required":false,"description":"[Passkeys mode] Passkey display name stored server-side"},{"schema":{"type":"string"},"in":"query","name":"keyDisplayName","required":false,"description":"[Passkeys mode] Passkey display label stored server-side"}],"responses":{"200":{"description":"Signup challenge (format depends on wallet mode)","content":{"application/json":{"schema":{"oneOf":[{"title":"Passkeys response","type":"object","properties":{"credentialRequestOptions":{"type":"object","description":"WebAuthn PublicKeyCredentialCreationOptions — pass to navigator.credentials.create()","additionalProperties":true}},"required":["credentialRequestOptions"]},{"title":"KDF response","type":"object","additionalProperties":true,"properties":{"userId":{"type":"string","description":"Internal user ID"},"externalUserId":{"type":"string","description":"External user ID (used in subsequent calls)"},"access_token":{"type":"string","description":"JWT access token"},"refresh_token":{"type":"string","description":"JWT refresh token"},"token_type":{"type":"string","description":"Always \"Bearer\""},"expires_in":{"type":"number","description":"Token expiry in seconds"},"hasPasskey":{"type":"boolean","description":"false for KDF mode"},"wallet":{"type":"string","description":"\"kdf\""},"salt":{"type":"string","description":"Hex-encoded salt for Argon2id key derivation"},"saltVersion":{"type":"number","description":"Salt version number"},"kdf":{"type":"object","description":"KDF parameters (Argon2id)","properties":{"algo":{"type":"string"},"memory":{"type":"number"},"iterations":{"type":"number"},"parallelism":{"type":"number"}}},"kdfParamsVersion":{"type":"number","description":"KDF params version"},"challenge":{"type":"string","description":"Server challenge to sign"},"challengeExpiresAt":{"type":"string","description":"ISO 8601 challenge expiry"},"serverSignature":{"type":"string","description":"HMAC signature of the challenge"},"serverKeyId":{"type":"string","description":"Server key ID used for HMAC"}}},{"title":"Email response","type":"object","additionalProperties":true,"properties":{"externalUserId":{"type":"string","description":"External user ID"},"access_token":{"type":"string"},"refresh_token":{"type":"string"},"hasPasskey":{"type":"boolean","description":"false for email mode"},"wallet":{"type":"string","description":"\"email\""},"authMethod":{"type":"string","description":"\"EMAIL_TOKEN_PENDING\""},"challenge":{"type":"string","description":"Server challenge to sign"},"challengeExpiresAt":{"type":"string"},"serverSignature":{"type":"string"},"serverKeyId":{"type":"string"},"emailOtpExpiresAt":{"type":"string","description":"ISO 8601 OTP expiry (30s)"},"emailOtp":{"type":"string","description":"[Local/test only] 6-digit OTP (hidden in production)"}}},{"title":"EOA 7702 response","type":"object","additionalProperties":true,"properties":{"wallet":{"type":"string","description":"\"7702\""},"challenge":{"type":"string","description":"SIWE-like message to sign with wallet"},"nonce":{"type":"string","description":"Unique nonce for replay protection"},"expiresAt":{"type":"string","description":"ISO 8601 challenge expiry (5 min)"},"address":{"type":"string","description":"EOA address echo"},"externalUserId":{"type":"string"},"hasPasskey":{"type":"boolean","description":"false"}}}]},"examples":{"Passkeys":{"summary":"wallet=passkeys (default)","value":{"credentialRequestOptions":{"rp":{"id":"passkeys-prat1.ibex.fi","name":"IBEx Wallet"},"user":{"id":"base64url-user-id","name":"user@example.com","displayName":"User"},"challenge":"base64url-challenge","pubKeyCredParams":[{"type":"public-key","alg":-7}],"timeout":60000,"attestation":"none","authenticatorSelection":{"authenticatorAttachment":"platform","residentKey":"required","userVerification":"required"}}}},"KDF":{"summary":"wallet=kdf","value":{"userId":"usr_abc123","externalUserId":"550e8400-e29b-41d4-a716-446655440000","access_token":"eyJ...","refresh_token":"eyJ...","token_type":"Bearer","expires_in":3600,"hasPasskey":false,"wallet":"kdf","salt":"a1b2c3d4e5f6...","saltVersion":1,"kdf":{"algo":"argon2id","memory":65536,"iterations":3,"parallelism":4},"kdfParamsVersion":1,"challenge":"Sign this message to verify your key...","challengeExpiresAt":"2026-03-13T15:10:00Z","serverSignature":"hmac-hex-signature...","serverKeyId":"server-key-1"}},"Email":{"summary":"wallet=email","value":{"externalUserId":"550e8400-e29b-41d4-a716-446655440000","access_token":"eyJ...","refresh_token":"eyJ...","hasPasskey":false,"wallet":"email","authMethod":"EMAIL_TOKEN_PENDING","challenge":"Sign this message to verify your key...","challengeExpiresAt":"2026-03-13T15:10:00Z","serverSignature":"hmac-hex-signature...","serverKeyId":"server-key-1","emailOtpExpiresAt":"2026-03-13T15:00:30Z","emailOtp":"123456"}},"EOA_7702":{"summary":"wallet=7702","value":{"wallet":"7702","challenge":"passkeys-prat1.ibex.fi wants you to sign in with your Ethereum account:\n0xCa33...Ab12\n\nSign in to IBEx Wallet\n\nURI: https://passkeys-prat1.ibex.fi\nVersion: 1\nChain ID: 11155111\nNonce: abc123def456\nIssued At: 2026-03-13T14:00:00Z\nExpiration Time: 2026-03-13T14:05:00Z","nonce":"abc123def456","expiresAt":"2026-03-13T14:05:00Z","address":"0xCa33...Ab12","externalUserId":"550e8400-e29b-41d4-a716-446655440000","hasPasskey":false}}}}}}}},"post":{"summary":"Sign-up with passkey 2/2","tags":["Authentication"],"description":"Complete signup (v1.2) — 3 modes.\n\n**Request body:**\n- `credential` (object, required for passkeys mode): WebAuthn credential from `navigator.credentials.create()`\n- `wallet` (string, optional): `passkeys` (default), `kdf`, `email`\n- `externalUserId` (string, required for `wallet=kdf` and `wallet=email`): ExternalUserId from GET /sign-up\n- `chainIds` (array, optional): Chain IDs for multi-chain Safe deployment\n- `chainId` (number, optional): Single chain ID (alternative to chainIds)\n- `keyName`, `keyDisplayName` (string, optional): Passkey metadata\n- `includeBalance`, `includeTransactions`, `includeUserdata` (boolean, optional): Include data in response\n\n**Response:**\n- Returns JWT tokens (`access_token`, `refresh_token`)\n- Includes `safeAddress` if passkey created (Safe wallet deployed)","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"wallet":{"type":"string","enum":["passkeys","kdf","email","7702","sms"]},"credential":{"type":"object"},"externalUserId":{"type":"string"},"telephone":{"type":"string","description":"E.164 phone number (required for wallet=sms)"},"email":{"type":"string","description":"Contact email (required for wallet=sms KYB mode)"},"companyRegistrationNumber":{"type":"string","description":"SIREN 9 digits (triggers KYB mode for wallet=sms)"},"phonePolicy":{"type":"string","enum":["frMobile","any"],"description":"Phone validation policy for wallet=sms"},"smsDryRun":{"type":"boolean","description":"If false, send a real SMS (default true in non-production: skip SMS, return code)"},"chainIds":{"type":"array","items":{"type":"number"}},"chainId":{"type":"number"},"keyName":{"type":"string"},"keyDisplayName":{"type":"string"},"includeBalance":{"type":"boolean"},"includeTransactions":{"type":"boolean"},"includeUserdata":{"type":"boolean"},"publicKey":{"type":"string"},"signature":{"type":"string"},"challenge":{"type":"string"},"challengeExpiresAt":{"type":"string"},"nonce":{"type":"string"},"timestamp":{"type":"number"},"serverSignature":{"type":"string"},"serverKeyId":{"type":"string"},"address":{"type":"string","description":"EOA address (required for wallet=7702)"},"provisioning":{"type":"object","description":"Optional advanced provisioning (passkeys mode only). If omitted, signup behavior remains unchanged.","properties":{"safes":{"type":"array","items":{"type":"object","required":["chainId"],"properties":{"chainId":{"type":"number"},"derive":{"type":"array","items":{"type":"object","required":["family"],"properties":{"family":{"type":"string"},"count":{"type":"number"}}}}}}},"global":{"type":"object","properties":{"chainId":{"type":"number","description":"Optional chain hint for client orchestration (global derivation itself is signer-scoped)."},"derive":{"type":"array","items":{"type":"object","required":["family"],"properties":{"family":{"type":"string"},"count":{"type":"number"}}}}}}}}}},"examples":{"Passkeys":{"summary":"Passkeys (default)","value":{"credential":{"id":"base64url-credential-id","rawId":"base64url-credential-raw-id","type":"public-key","authenticatorAttachment":"platform","clientExtensionResults":{},"response":{"attestationObject":"base64url-attestation-object","clientDataJSON":"base64url-client-data-json"}},"chainId":100,"keyName":"alice@foo.domain","keyDisplayName":"Alice","provisioning":{"safes":[{"chainId":100,"derive":[{"family":"EVM","count":2}]},{"chainId":421614,"derive":[{"family":"SOLANA","count":1}]}],"global":{"chainId":1,"derive":[{"family":"SOLANA","count":2},{"family":"BITCOIN_P2WPKH","count":1}]}}}},"Kdf":{"summary":"wallet=kdf","value":{"wallet":"kdf","externalUserId":"externalUserId-uuid","publicKey":"0xYourDerivedAddress","signature":"0xSignedCanonicalMessage","challenge":"base64-from-get","challengeExpiresAt":"2025-12-12T12:52:28.607Z","nonce":"base64-random","timestamp":1765543649,"serverSignature":"base64-from-get","serverKeyId":"pin-kdf-hmac-v1","saltVersion":1,"kdfParamsVersion":1}},"Email":{"summary":"wallet=email","value":{"wallet":"email","externalUserId":"externalUserId-uuid","publicKey":"0xYourDerivedAddress","signature":"0xSignedCanonicalMessage","challenge":"base64-from-get","challengeExpiresAt":"2025-12-12T12:52:21.697Z","nonce":"base64-random","timestamp":1765543642,"serverSignature":"base64-from-get","serverKeyId":"email-token-hmac-v1"}},"EOA7702":{"summary":"wallet=7702 (EOA + EIP-7702)","value":{"wallet":"7702","address":"0xCa3384350beC79971C13a1710FC4868eD84f497F","signature":"0xSignedSIWEChallenge...","nonce":"abc123def456789","chainId":11155111}},"SmsKyc":{"summary":"wallet=sms (KYC individual)","value":{"wallet":"sms","telephone":"+33612345678"}},"SmsKyb":{"summary":"wallet=sms (KYB company)","value":{"wallet":"sms","telephone":"+33612345678","email":"contact@entreprise.fr","companyRegistrationNumber":"123456789"}}}}}},"responses":{"200":{"description":"Default Response"}}}},"/v1.2/auth/sign-in":{"get":{"summary":"Sign-in with passkey 1/2","tags":["Authentication"],"description":"Sign-in endpoint (v1.2) supporting 4 authentication methods.\n\n**rpId (tenant identification):**\nThe rpId (Relying Party Identifier) is a domain string that scopes users, wallets, and credentials to your application. Each rpId is an isolated namespace — users created under one rpId are invisible to another. It also serves as the WebAuthn rpId, so the browser enforces that it matches your page origin.\n\nYour rpId must be registered with IBEx. Pass it via:\n- Query parameter: `?rpId=yourdomain.com`\n- Header: `X-RpId: yourdomain.com`\n- Automatically via Origin header (browser requests)\n\nIf missing or unregistered, returns `400 Unknown domain/rpId`.\nFor local development, `localhost` is automatically recognized.\nSee the full [rpId guide](/docs/guides/integration/rpid) for production setup, WebAuthn constraints, and common errors.\n\n---\n\n**Query parameters reference:**\n\n| Parameter | Type | Required for |\n|-----------|------|-------------|\n| `wallet` | string | All modes (`passkeys` default, `kdf`, `email`, `7702`) |\n| `externalUserId` | string | `wallet=kdf` and `wallet=email` |\n| `address` | string | `wallet=7702` only (0x-prefixed, 42 chars) |\n| `chainId` | number | `wallet=7702` (optional) |\n| `flow` | string | ⚠️ Deprecated — use `wallet=kdf` instead |\n\n---\n\n### 1. Passkeys Safe wallet (`wallet=passkeys`, default)\n\n**What it means?**\nAuthenticates a returning user via their registered WebAuthn passkey (biometric / security key). The server generates a challenge that the browser signs with the stored private key. This proves possession without ever transmitting the key.\n\n**Next step:** POST the `credential` returned by `navigator.credentials.get()` to `POST /sign-in`.\n\n**Example:** `GET /v1.2/auth/sign-in`\n\n**Important — PRF extension (passkeys mode):**\nThe `extensions.prf.eval.first` value in the response is a **base64url-encoded string** (JSON cannot represent binary).\nBefore passing to `navigator.credentials.get()`, you **must** decode it to `ArrayBuffer`:\n\n```js\nimport { base64URLStringToBuffer } from '@simplewebauthn/browser';\nconst opts = response.credentialRequestOptions;\nif (opts.extensions?.prf?.eval?.first) {\n    opts.extensions.prf.eval.first = base64URLStringToBuffer(opts.extensions.prf.eval.first);\n    if (opts.extensions.prf.eval.second)\n        opts.extensions.prf.eval.second = base64URLStringToBuffer(opts.extensions.prf.eval.second);\n}\n```\n\nWithout this conversion, the browser will throw: `Failed to read the 'prf' property: The provided value is not of type '(ArrayBuffer or ArrayBufferView)'`.\n\n---\n\n### 2. KDF Safe wallet (`wallet=kdf`)\n\n**What it means?**\nAuthenticates a user whose signer was derived from a PIN/password. The server returns the same salt and KDF parameters used at sign-up so the client can re-derive the exact same key and sign a fresh challenge.\n\n**Next step:** Re-derive the key from PIN + salt, sign the canonical message, and call `POST /sign-in` with the signature proof.\n\n**Example:** `GET /v1.2/auth/sign-in?wallet=kdf&externalUserId=<uuid>`\n\n---\n\n### 3. Email Safe wallet (`wallet=email`)\n\n**What it means?**\nAuthenticates a user who signed up via the email-token flow. The server sends a new email OTP (30s) and returns a challenge. After verifying the OTP via `POST /email/recover`, the client retrieves its encrypted backup and re-derives the key to sign the challenge.\n\n**Next step:** Verify OTP via `POST /email/recover`, then call `POST /sign-in` with the signature proof.\n\n**Example:** `GET /v1.2/auth/sign-in?wallet=email&externalUserId=<uuid>`\n\n---\n\n### 4. EOA + EIP-7702 (`wallet=7702`)\n\n**What it means?**\nAuthenticates a user who registered via their EOA (external wallet). The server looks up the existing `EOA_7702` signer for the given address and domain, then generates a SIWE-like challenge. The user signs it with their wallet (MetaMask, WalletConnect, etc.) to prove ownership.\n\n**Next step:** Sign `challenge` with the wallet and call `POST /sign-in` with `{ wallet: \"7702\", address, signature, nonce }`.\n\n**Example:** `GET /v1.2/auth/sign-in?wallet=7702&address=0xCa33...`\n\n---\n\n### Response enrichment (POST sign-in, step 2)\n\nWhen completing sign-in via `POST /sign-in` (step 2), you can include optional boolean flags in the request body to receive additional data alongside the JWT tokens:\n\n| Flag | Type | What it adds |\n|------|------|-------------|\n| `includeBalance` | boolean | `balance` — all monitored token balances for the wallet on the requested chain, **including tokens with zero balance**. This effectively gives you the full list of the user's watched tokens/addresses. |\n| `includeTransactions` | boolean | `transactions` — paginated transaction history for the wallet |\n| `includeUserdata` | boolean | `userdata` — same aggregated payload format as `GET /v1.2/users/me` |\n\nThis avoids extra API calls after sign-in — you get JWT tokens + enriched data in a single response. Use `chainId` in the POST body to select which chain to query (defaults to the platform's default chain).\n\n**Example POST body (passkeys mode with enrichment):**\n```json\n{\n  \"credential\": { \"id\": \"...\", \"rawId\": \"...\", \"type\": \"public-key\", \"response\": { ... } },\n  \"chainId\": 421614,\n  \"includeBalance\": true,\n  \"includeUserdata\": true\n}\n```\n\nSee `POST /sign-in` for full details and response examples.\n\n---\n\n### Automatic multi-chain Safe expansion (passkey & KDF modes)\n\nWhen a user signs in with a passkey or KDF wallet (`walletMode=SAFE_4337`), the API automatically checks whether their Safe wallet exists on all active chains configured in the platform.\n\n**How it works:**\n- After sign-in, the API compares the signer's existing Safes against all chains marked `isActive=true` and `isSafeWallet=true`.\n- For any missing chain, the deterministic Safe address is computed and persisted — no on-chain deployment needed (counterfactual).\n- The new Safe addresses are registered in BCReader for balance/transaction monitoring.\n- This runs **asynchronously** (non-blocking) — the sign-in response is returned immediately.\n\n**For integrators:**\n- After sign-in, call `GET /v1.2/users/me/address` to retrieve the full list of wallets across all chains.\n- If the expansion is still in progress, retry after 2–3 seconds.\n- EOA/EIP-7702 wallets are excluded from this mechanism.","parameters":[{"schema":{"type":"string","enum":["passkeys","kdf","email","7702","sms"]},"in":"query","name":"wallet","required":false,"description":"Authentication mode: passkeys (default), kdf, email, 7702, or sms"},{"schema":{"type":"string"},"in":"query","name":"externalUserId","required":false,"description":"[KDF / Email mode] User identifier from a previous sign-up"},{"schema":{"type":"string","enum":["pin-kdf"],"deprecated":true},"in":"query","name":"flow","required":false,"description":"[Deprecated] Use wallet=kdf instead"},{"schema":{"type":"string"},"in":"query","name":"address","required":false,"description":"[7702 mode only] EOA address (0x-prefixed, 42 chars)"},{"schema":{"type":"number"},"in":"query","name":"chainId","required":false,"description":"[7702 mode only] Target blockchain chain ID"},{"schema":{"type":"string"},"in":"query","name":"telephone","required":false,"description":"[SMS mode only] E.164 phone number"},{"schema":{"type":"string","enum":["frMobile","any"]},"in":"query","name":"phonePolicy","required":false,"description":"[SMS mode only] Phone validation policy"},{"schema":{"type":"boolean"},"in":"query","name":"smsDryRun","required":false,"description":"[SMS mode only] If false, enforce rate limits and send real SMS. Default: true outside production/preprod."}],"responses":{"200":{"description":"Sign-in challenge (format depends on wallet mode)","content":{"application/json":{"schema":{"oneOf":[{"title":"Passkeys response","type":"object","properties":{"credentialRequestOptions":{"type":"object","description":"WebAuthn PublicKeyCredentialRequestOptions — pass to navigator.credentials.get()","additionalProperties":true}},"required":["credentialRequestOptions"]},{"title":"KDF response","type":"object","additionalProperties":true,"properties":{"externalUserId":{"type":"string","description":"External user ID"},"wallet":{"type":"string","description":"\"kdf\""},"flow":{"type":"string","description":"\"pin-kdf\" (legacy compat)"},"salt":{"type":"string","description":"Hex-encoded salt for Argon2id"},"saltVersion":{"type":"number"},"kdf":{"type":"object","description":"KDF parameters","properties":{"algo":{"type":"string"},"memory":{"type":"number"},"iterations":{"type":"number"},"parallelism":{"type":"number"}}},"kdfParamsVersion":{"type":"number"},"challenge":{"type":"string","description":"Server challenge to sign"},"challengeExpiresAt":{"type":"string","description":"ISO 8601 expiry"},"serverSignature":{"type":"string","description":"HMAC signature"},"serverKeyId":{"type":"string"},"hasPasskey":{"type":"boolean"}}},{"title":"Email response","type":"object","additionalProperties":true,"properties":{"externalUserId":{"type":"string"},"wallet":{"type":"string","description":"\"email\""},"challenge":{"type":"string"},"challengeExpiresAt":{"type":"string"},"serverSignature":{"type":"string"},"serverKeyId":{"type":"string"},"emailOtpExpiresAt":{"type":"string","description":"OTP expiry (30s)"},"emailOtp":{"type":"string","description":"[Local/test only] 6-digit OTP"},"hasPasskey":{"type":"boolean"}}},{"title":"EOA 7702 response","type":"object","additionalProperties":true,"properties":{"wallet":{"type":"string","description":"\"7702\""},"challenge":{"type":"string","description":"SIWE-like message to sign"},"nonce":{"type":"string","description":"Unique nonce"},"expiresAt":{"type":"string","description":"ISO 8601 expiry (5 min)"},"externalUserId":{"type":"string"},"hasPasskey":{"type":"boolean","description":"false"}}}]},"examples":{"Passkeys":{"summary":"wallet=passkeys (default)","value":{"credentialRequestOptions":{"rpId":"passkeys-prat1.ibex.fi","challenge":"base64url-challenge","timeout":60000,"allowCredentials":[{"id":"base64url-cred-id","type":"public-key"}],"userVerification":"required"}}},"KDF":{"summary":"wallet=kdf","value":{"externalUserId":"550e8400-e29b-41d4-a716-446655440000","wallet":"kdf","flow":"pin-kdf","salt":"a1b2c3d4e5f6...","saltVersion":1,"kdf":{"algo":"argon2id","memory":65536,"iterations":3,"parallelism":4},"kdfParamsVersion":1,"challenge":"Sign this message to verify your key...","challengeExpiresAt":"2026-03-13T15:10:00Z","serverSignature":"hmac-hex-signature...","serverKeyId":"pin-kdf-hmac-v1","hasPasskey":false}},"Email":{"summary":"wallet=email","value":{"externalUserId":"550e8400-e29b-41d4-a716-446655440000","wallet":"email","challenge":"Sign this message to verify your key...","challengeExpiresAt":"2026-03-13T15:10:00Z","serverSignature":"hmac-hex-signature...","serverKeyId":"email-token-hmac-v1","emailOtpExpiresAt":"2026-03-13T15:00:30Z","emailOtp":"123456","hasPasskey":false}},"EOA_7702":{"summary":"wallet=7702","value":{"wallet":"7702","challenge":"passkeys-prat1.ibex.fi wants you to sign in with your Ethereum account:\n0xCa33...Ab12\n\nSign in to IBEx Wallet\n\nURI: https://passkeys-prat1.ibex.fi\nVersion: 1\nChain ID: 11155111\nNonce: abc123def456\nIssued At: 2026-03-13T14:00:00Z\nExpiration Time: 2026-03-13T14:05:00Z","nonce":"abc123def456","expiresAt":"2026-03-13T14:05:00Z","externalUserId":"550e8400-e29b-41d4-a716-446655440000","hasPasskey":false}},"SMS":{"summary":"wallet=sms","value":{"wallet":"sms"}}}}}}}},"post":{"summary":"Sign-in with passkey 2/2","tags":["Authentication"],"description":"Complete sign-in (v1.2) — 3 modes.\n\n**Request body:**\n- `credential` (object, required for passkeys mode): WebAuthn credential from `navigator.credentials.get()`\n- `wallet` (string, optional): `passkeys` (default), `kdf`, `email`\n- `externalUserId` (string, required for `wallet=kdf` and `wallet=email`)\n- `chainId` (number, optional): Target blockchain ID\n- `intent` (string, optional): set to `safe_provision` to receive a short-lived `safe_provision_token` for `POST /v1.2/auth/sign-in/safe-provision` (PASSKEY + SAFE_4337 only)\n- `safeAddress` (string, optional): pin the session Login to this Safe (must belong to signer on `chainId`)\n- `deploySaltNonce` (string, optional): select Safe by salt slot on `chainId` (`\"\"` = primary / default salt)\n- `includeBalance`, `includeTransactions`, `includeUserdata` (boolean, optional): Include data in response\n- `asyncData` (boolean, optional): when true and include flags are set, returns quickly with `dataRequestId`; poll `GET /v1.2/auth/sign-in/data/:dataRequestId` for heavy data\n\n**Exemples (4 modes)**\n\n1. Mode passkeys (défaut)\n\nPOST body:\n```json\n{\n  \"credential\": { \"id\": \"base64url...\", \"rawId\": \"base64url...\", \"type\": \"public-key\", \"response\": { \"authenticatorData\": \"base64url...\", \"clientDataJSON\": \"base64url...\", \"signature\": \"base64url...\", \"userHandle\": \"base64url...\" } },\n  \"chainId\": 421614,\n  \"includeBalance\": true,\n  \"includeUserdata\": true\n}\n```\n\nResponse 200:\n```json\n{\n  \"access_token\": \"eyJ...\",\n  \"refresh_token\": \"eyJ...\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600,\n  \"issuer\": \"foo.domain\",\n  \"audience\": \"foo.domain\",\n  \"subject\": \"externalUserId-uuid\",\n  \"roles\": [\"USER\"],\n  \"authMethod\": \"PASSKEY\",\n  \"hasPasskey\": true,\n  \"chainId\": 421614,\n  \"safeAddress\": {\n    \"421614\": \"0xd4c819A7f5A1dC2E55D89513E9a0B38fadd622E7\",\n    \"8453\": \"0x7C4755101468f4fD8b8E739165926D81851Ccc0F\",\n    \"100\": \"0x1923FD4e893fd7bb5a77cd4a9804D1bB5f20e33a\"\n  },\n  \"eoaAddress\": \"0xa6D9e8Ed50F21391047da545C4Eb3Fd0d258560b\",\n  \"eoaAddresses\": [\n    { \"type\": \"EVM\", \"address\": \"0xa6D9e8Ed50F21391047da545C4Eb3Fd0d258560b\" },\n    { \"type\": \"SOLANA\", \"address\": \"5tUYndEfq8hFwsQTtcwg9Zugu4hPf6sw6sggrXkSpWn3\" },\n    { \"type\": \"BITCOIN_P2WPKH\", \"address\": \"bc1q88hhscjmxt8df5aaye8svjxut7lzlmrjh5preg\" },\n    { \"type\": \"BITCOIN_P2TR\", \"address\": \"bc1pc3dwu5j7q6vv7v5f4majx262pe9hu0cgvsgjfl0khpu2hu5v8erqxeyvl3\" },\n    { \"type\": \"BITCOIN_P2WPKH_TESTNET\", \"address\": \"tb1q88hhscjmxt8df5aaye8svjxut7lzlmrjaj6szm\" },\n    { \"type\": \"BITCOIN_P2TR_TESTNET\", \"address\": \"tb1pc3dwu5j7q6vv7v5f4majx262pe9hu0cgvsgjfl0khpu2hu5v8erq33jr97\" }\n  ],\n  \"prfCapable\": false,\n  \"keyName\": \"foo.domain abc123\",\n  \"keyDisplayName\": \"foo.domain abc123\",\n  \"walletMode\": \"SAFE_4337\",\n  \"services\": [\n    { \"isPrivateLending\": true },\n    { \"isTransfer\": true },\n    { \"isSwap\": true },\n    { \"isLending\": true }\n  ]\n}\n```\n\n> **Note:** \\`eoaAddresses\\` lists all derived addresses from the passkey. Available types depend on the key derivation at sign-up. The list can be filtered server-side via the \\`WALLET_TYPE_SIGNIN\\` env (CSV of allowed types).\n\n2. Mode wallet=kdf\n\nPOST body:\n```json\n{\n  \"wallet\": \"kdf\",\n  \"externalUserId\": \"externalUserId-uuid\",\n  \"publicKey\": \"0xYourDerivedAddress\",\n  \"signature\": \"0xSignedCanonicalMessage\",\n  \"challenge\": \"base64-from-get\",\n  \"challengeExpiresAt\": \"2025-12-12T12:59:00.000Z\",\n  \"nonce\": \"base64-random\",\n  \"timestamp\": 1765544000,\n  \"serverSignature\": \"base64-from-get\",\n  \"serverKeyId\": \"pin-kdf-hmac-v1\",\n  \"saltVersion\": 1,\n  \"kdfParamsVersion\": 1\n}\n```\n\nResponse 200:\n```json\n{\n  \"access_token\": \"eyJ...\",\n  \"refresh_token\": \"eyJ...\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600,\n  \"issuer\": \"foo.domain\",\n  \"audience\": \"foo.domain\",\n  \"subject\": \"externalUserId-uuid\",\n  \"roles\": [\"USER\"],\n  \"authMethod\": \"PIN_KDF\",\n  \"hasPasskey\": false,\n  \"wallet\": \"kdf\",\n  \"flow\": \"pin-kdf\",\n  \"publicKey\": \"0xyourderivedaddress\",\n  \"signerVersion\": 1,\n  \"chainId\": 421614,\n  \"safeAddress\": { \"421614\": \"0xd4c819A7f5A1dC2E55D89513E9a0B38fadd622E7\" },\n  \"eoaAddress\": \"0xYourDerivedAddress\",\n  \"eoaAddresses\": [{ \"type\": \"EVM\", \"address\": \"0xYourDerivedAddress\" }],\n  \"walletMode\": \"SAFE_4337\",\n  \"services\": [{ \"isPrivateLending\": true }, { \"isTransfer\": true }, { \"isSwap\": true }, { \"isLending\": true }]\n}\n```\n\n---\n\n3. Mode wallet=email\n\nPOST body:\n```json\n{\n  \"wallet\": \"email\",\n  \"externalUserId\": \"externalUserId-uuid\",\n  \"publicKey\": \"0xYourDerivedAddress\",\n  \"signature\": \"0xSignedCanonicalMessage\",\n  \"challenge\": \"base64-from-get\",\n  \"challengeExpiresAt\": \"2025-12-12T12:59:00.000Z\",\n  \"nonce\": \"base64-random\",\n  \"timestamp\": 1765544000,\n  \"serverSignature\": \"base64-from-get\",\n  \"serverKeyId\": \"email-token-hmac-v1\"\n}\n```\n\nResponse 200:\n```json\n{\n  \"access_token\": \"eyJ...\",\n  \"refresh_token\": \"eyJ...\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600,\n  \"issuer\": \"foo.domain\",\n  \"audience\": \"foo.domain\",\n  \"subject\": \"externalUserId-uuid\",\n  \"roles\": [\"USER\"],\n  \"authMethod\": \"EMAIL_TOKEN\",\n  \"hasPasskey\": false,\n  \"wallet\": \"email\",\n  \"publicKey\": \"0xyourderivedaddress\",\n  \"signerVersion\": 1,\n  \"chainId\": 421614,\n  \"safeAddress\": { \"421614\": \"0xd4c819A7f5A1dC2E55D89513E9a0B38fadd622E7\" },\n  \"eoaAddress\": \"0xYourDerivedAddress\",\n  \"eoaAddresses\": [{ \"type\": \"EVM\", \"address\": \"0xYourDerivedAddress\" }],\n  \"walletMode\": \"SAFE_4337\",\n  \"services\": [{ \"isPrivateLending\": true }, { \"isTransfer\": true }, { \"isSwap\": true }, { \"isLending\": true }]\n}\n```\n\n---\n\n4. Mode wallet=7702 (EOA + EIP-7702)\n\nPOST body:\n```json\n{\n  \"wallet\": \"7702\",\n  \"address\": \"0xCa3384350beC79971C13a1710FC4868eD84f497F\",\n  \"signature\": \"0xSignedSIWEChallenge...\",\n  \"nonce\": \"abc123def456789\"\n}\n```\n\nResponse 200:\n```json\n{\n  \"access_token\": \"eyJ...\",\n  \"refresh_token\": \"eyJ...\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600,\n  \"issuer\": \"foo.domain\",\n  \"audience\": \"foo.domain\",\n  \"subject\": \"externalUserId-uuid\",\n  \"roles\": [\"USER\"],\n  \"authMethod\": \"EOA_7702\",\n  \"hasPasskey\": false,\n  \"wallet\": \"7702\",\n  \"walletMode\": \"EOA_7702\",\n  \"eoaAddress\": \"0xCa3384350beC79971C13a1710FC4868eD84f497F\",\n  \"safeAddress\": { \"11155111\": \"0xCa3384350beC79971C13a1710FC4868eD84f497F\" },\n  \"delegations\": [{ \"status\": \"ACTIVE\", \"delegateAddress\": \"0xSafeEIP7702Proxy\", \"chainId\": 11155111, \"txHash\": \"0x...\", \"eoaAddress\": \"0xCa33...\" }]\n}\n```\n\n---\n\n### Base response fields reference\n\nAll sign-in modes return these base JWT fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| \\`access_token\\` | string | JWT access token |\n| \\`refresh_token\\` | string | JWT refresh token |\n| \\`token_type\\` | string | Always \"Bearer\" |\n| \\`expires_in\\` | number | Token TTL in seconds |\n| \\`issuer\\` | string | rpId (domain) |\n| \\`audience\\` | string | rpId (domain) |\n| \\`subject\\` | string | externalUserId |\n| \\`roles\\` | string[] | User roles ([\"USER\"]) |\n| \\`authMethod\\` | string | PASSKEY, PIN_KDF, EMAIL_TOKEN, or EOA_7702 |\n| \\`hasPasskey\\` | boolean | Whether user has a passkey signer |\n\n### Enrichment fields (added automatically)\n\n| Field | Type | Description |\n|-------|------|-------------|\n| \\`chainId\\` | number | Active blockchain ID for this session |\n| \\`safeAddress\\` | object | Map of chainId to Safe wallet address (all chains) |\n| \\`eoaAddress\\` | string | Primary EVM EOA address (derived from passkey/key) |\n| \\`eoaAddresses\\` | array | All derived addresses: [{ type, address }]. Types: EVM, SOLANA, BITCOIN_P2WPKH, BITCOIN_P2TR, BITCOIN_P2WPKH_TESTNET, BITCOIN_P2TR_TESTNET, COSMOS, POLKADOT, TEZOS_TZ1, TEZOS_TZ2, TEZOS_TZ3, NEAR, STELLAR, CARDANO |\n| \\`prfCapable\\` | boolean | Whether the passkey supports PRF extension |\n| \\`keyName\\` | string | Passkey human-friendly name |\n| \\`keyDisplayName\\` | string | Passkey display name |\n| \\`walletMode\\` | string | SAFE_4337 or EOA_7702 |\n| \\`services\\` | array | Domain feature flags: isPrivateLending, isTransfer, isSwap, isLending |\n\n---\n\n### Optional enrichment flags (POST body)\n\nEnriched response examples (what each flag adds):\n\n| Flag | Type | What it adds |\n|------|------|-------------|\n| \\`includeBalance\\` | boolean | \\`balance\\` — all monitored token balances for the wallet on the requested chain, including tokens with zero balance |\n| \\`includeTransactions\\` | boolean | \\`transactions\\` — paginated transaction history for the wallet |\n| \\`includeUserdata\\` | boolean | \\`userdata\\` — same aggregated payload format as \\`GET /v1.2/users/me\\` |\n\nUse \\`chainId\\` in the POST body to select which chain to query (defaults to the platform's default chain).\n\n1) When \\`includeBalance=true\\` → adds \\`balance\\`\n\nReturns all monitored token balances for the user's Safe wallet on the requested chain, **including tokens with zero balance**.\n\n```json\n{\n  \"...base + enrichment fields...\": \"...\",\n  \"balance\": {\n    \"0x420ca0f9b9b604ce0fd9c18ef134c705e5fa3430\": { \"address\": \"0x420c...\", \"balance\": \"123.45\" },\n    \"0xabcdefabcdefabcdefabcdefabcdefabcdefabcd\": { \"address\": \"0xabcd...\", \"balance\": \"0\" }\n  }\n}\n```\n\n2) When \\`includeTransactions=true\\` → adds \\`transactions\\`\n\n```json\n{\n  \"...base + enrichment fields...\": \"...\",\n  \"transactions\": {\n    \"total\": 2, \"page\": 1, \"limit\": 2, \"totalPages\": 1,\n    \"data\": [{ \"hash\": \"0x...\", \"timestamp\": 1700000000 }, { \"hash\": \"0x...\", \"timestamp\": 1700000100 }]\n  }\n}\n```\n\n3) When \\`includeUserdata=true\\` → adds \\`userdata\\`\n\nReturns the same aggregator payload as \\`GET /v1.2/users/me\\` (sections \\`{ status, data }\\` per sub-endpoint).\n\n```json\n{\n  \"...base + enrichment fields...\": \"...\",\n  \"userdata\": {\n    \"addresses\": { \"status\": 200, \"data\": { \"count\": 1, \"wallets\": [] } },\n    \"balances\": { \"status\": 200, \"data\": { \"type\": \"crypto\", \"identifier\": \"0x...\" } },\n    \"ibans\": { \"status\": 200, \"data\": { \"count\": 0, \"ibans\": [] } },\n    \"lending\": { \"status\": 200, \"data\": [] },\n    \"pools\": { \"status\": 200, \"data\": { \"type\": \"crypto\", \"data\": [] } },\n    \"signers\": { \"status\": 200, \"data\": { \"count\": 1, \"signers\": [] } },\n    \"transactions\": { \"status\": 200, \"data\": { \"type\": \"crypto\", \"total\": 0, \"data\": [] } },\n    \"kycStatus\": { \"status\": 200, \"data\": { \"kycLevel\": \"0\", \"verified\": false } },\n    \"addressbook\": { \"status\": 200, \"data\": { \"success\": true, \"data\": [] } }\n  }\n}\n```\n\n---\n\n**Automatic multi-chain Safe expansion:**\nAfter a successful sign-in (passkey or KDF mode, \\`walletMode=SAFE_4337\\`), the API **automatically expands** the user's Safe wallet to all active chains. This is non-blocking — the JWT response is returned immediately while expansion runs in the background.\n- Call \\`GET /v1.2/users/me/address\\` after sign-in to see all wallets across all chains.\n- If the expansion is still in progress, retry after 2–3 seconds.\n- EOA/EIP-7702 wallets are excluded from this mechanism.\n","requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"credential":{"type":"object"},"chainId":{"type":"number"},"includeBalance":{"type":"boolean"},"includeTransactions":{"type":"boolean"},"includeUserdata":{"type":"boolean"},"asyncData":{"type":"boolean"},"intent":{"type":"string","description":"Set to `safe_provision` to receive `safe_provision_token` for POST /v1.2/auth/sign-in/safe-provision."},"safeAddress":{"type":"string","description":"Optional Safe to attach the session to (must belong to signer on selected chain)."},"deploySaltNonce":{"type":"string","description":"Optional Safe slot selector on selected chain (`\"\"` = primary Safe)."}},"required":["credential"],"additionalProperties":true},{"type":"object","properties":{"wallet":{"type":"string","enum":["kdf"]},"externalUserId":{"type":"string"},"chainId":{"type":"number"},"includeBalance":{"type":"boolean"},"includeTransactions":{"type":"boolean"},"includeUserdata":{"type":"boolean"},"asyncData":{"type":"boolean"},"publicKey":{"type":"string"},"signature":{"type":"string"},"challenge":{"type":"string"},"challengeExpiresAt":{"type":"string"},"saltVersion":{"type":"number"},"kdfParamsVersion":{"type":"number"},"nonce":{"type":"string"},"timestamp":{"type":"number"},"serverSignature":{"type":"string"},"serverKeyId":{"type":"string"}},"required":["wallet","externalUserId","publicKey","signature","challenge","nonce","timestamp","serverSignature","serverKeyId"],"additionalProperties":true},{"type":"object","properties":{"wallet":{"type":"string","enum":["email"]},"externalUserId":{"type":"string"},"chainId":{"type":"number"},"includeBalance":{"type":"boolean"},"includeTransactions":{"type":"boolean"},"includeUserdata":{"type":"boolean"},"asyncData":{"type":"boolean"},"publicKey":{"type":"string"},"signature":{"type":"string"},"challenge":{"type":"string"},"challengeExpiresAt":{"type":"string"},"nonce":{"type":"string"},"timestamp":{"type":"number"},"serverSignature":{"type":"string"},"serverKeyId":{"type":"string"}},"required":["wallet","externalUserId","publicKey","signature","challenge","nonce","timestamp","serverSignature","serverKeyId"],"additionalProperties":true},{"type":"object","properties":{"wallet":{"type":"string","enum":["7702"]},"address":{"type":"string","description":"EOA address (0x-prefixed, 42 chars)"},"signature":{"type":"string","description":"ECDSA signature of the SIWE challenge"},"nonce":{"type":"string","description":"Nonce from GET /sign-in?wallet=7702"},"chainId":{"type":"number"},"includeBalance":{"type":"boolean"},"includeTransactions":{"type":"boolean"},"includeUserdata":{"type":"boolean"},"asyncData":{"type":"boolean"}},"required":["wallet","address","signature","nonce"],"additionalProperties":true},{"type":"object","properties":{"wallet":{"type":"string","enum":["sms"]},"telephone":{"type":"string","description":"E.164 phone number (same as GET step)"},"code":{"type":"string","description":"4-8 digit OTP received by SMS"},"phonePolicy":{"type":"string","enum":["frMobile","any"],"description":"Phone validation policy"}},"required":["wallet","telephone","code"],"additionalProperties":true}]},"examples":{"Passkeys":{"summary":"Passkeys (default)","value":{"credential":{"id":"base64url-credential-id","rawId":"base64url-credential-raw-id","type":"public-key","authenticatorAttachment":"platform","clientExtensionResults":{},"response":{"authenticatorData":"base64url-authenticator-data","clientDataJSON":"base64url-client-data-json","signature":"base64url-signature","userHandle":"base64url-user-handle"}}}},"Kdf":{"summary":"wallet=kdf","value":{"wallet":"kdf","externalUserId":"externalUserId-uuid","publicKey":"0xYourDerivedAddress","signature":"0xSignedCanonicalMessage","challenge":"base64-from-get","challengeExpiresAt":"2025-12-12T12:59:00.000Z","nonce":"base64-random","timestamp":1765544000,"serverSignature":"base64-from-get","serverKeyId":"pin-kdf-hmac-v1","saltVersion":1,"kdfParamsVersion":1}},"Email":{"summary":"wallet=email","value":{"wallet":"email","externalUserId":"externalUserId-uuid","publicKey":"0xYourDerivedAddress","signature":"0xSignedCanonicalMessage","challenge":"base64-from-get","challengeExpiresAt":"2025-12-12T12:59:00.000Z","nonce":"base64-random","timestamp":1765544000,"serverSignature":"base64-from-get","serverKeyId":"email-token-hmac-v1"}},"EOA7702":{"summary":"wallet=7702 (EOA + EIP-7702)","value":{"wallet":"7702","address":"0xCa3384350beC79971C13a1710FC4868eD84f497F","signature":"0xSignedSIWEChallenge...","nonce":"abc123def456789"}},"SMS":{"summary":"wallet=sms (SMS OTP)","value":{"wallet":"sms","telephone":"+33612345678","code":"123456"}}}}}},"parameters":[{"schema":{"type":"string"},"in":"query","name":"intent","required":false,"description":"Optional shortcut for passkeys mode. `safe_provision` enables issuance of `safe_provision_token` in response."}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/auth/email/recover":{"post":{"summary":"Recover encrypted wallet backup (wallet=email)","tags":["Authentication"],"description":"wallet=email recovery bootstrap.\n\n1) GET /sign-in?wallet=email&externalUserId=... → sends one-shot email OTP (30s)\n2) POST /email/recover with {externalUserId, emailOtp} → returns encrypted backup (C+salt) + challenge\n3) Client decrypts locally with user secret, derives publicKey, signs canonical challenge, then POST /sign-in (wallet=email).","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["externalUserId","emailOtp"],"properties":{"externalUserId":{"type":"string"},"emailOtp":{"type":"string"}},"additionalProperties":true}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"externalUserId":{"type":"string"},"wallet":{"type":"string"},"encryptedPrivateKey":{"type":"string"},"backupSalt":{"type":"string"},"backupKdf":{"type":"object","additionalProperties":true},"challenge":{"type":"string"},"challengeExpiresAt":{"type":"string"},"serverSignature":{"type":"string"},"serverKeyId":{"type":"string"}},"additionalProperties":true}}}}}}},"/v1.2/auth/sign-in/data/{dataRequestId}":{"get":{"summary":"Poll asynchronous sign-in include data","tags":["Authentication"],"description":"Returns the state of an async sign-in enrichment request created by POST /sign-in with asyncData=true.","parameters":[{"schema":{"type":"string"},"in":"path","name":"dataRequestId","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"status":{"type":"string","enum":["PENDING","READY","FAILED"]},"data":{"type":"object","additionalProperties":true},"error":{"type":"string"},"createdAt":{"type":"string"},"completedAt":{"type":"string"},"failedAt":{"type":"string"},"expiresAt":{"type":"string"}}}}}}}}},"/v1.2/auth/sign-in/safe-provision":{"post":{"summary":"Register additional counterfactual Safe (same passkey signer)","tags":["Authentication"],"description":"Completes the two-step flow after `POST /v1.2/auth/sign-in` with `intent=safe_provision`.\n\nSend `Authorization: Bearer <safe_provision_token>` and a `saltNonce` (decimal string, >= 1) to derive a new Safe4337 address on the given `blockchainId` for the same signer.\n\nThe provision token is single-use and short-lived.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["blockchainId","saltNonce"],"properties":{"blockchainId":{"type":"number","description":"Target EVM chain id"},"saltNonce":{"type":"string","description":"CREATE2 salt for predicted Safe (decimal, >= 1). Must be unique per signer+chain."}}}}},"required":true},"parameters":[{"schema":{"type":"string"},"in":"header","name":"authorization","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"address":{"type":"string"},"blockchainId":{"type":"number"},"saltNonce":{"type":"string"},"deploySaltNonce":{"type":"string"},"safe_provision_expires_in":{"type":"number","description":"Reference TTL (same as step-1 token)"}}}}}}}}},"/v1.2/auth/refresh":{"post":{"summary":"Refresh Session","tags":["Authentication"],"description":"Initial sign-in JWT is for a maximum duration of 1h. Need to refresh to avoid session expiration.\n\n**How it works:**\nSend the `refresh_token` received during Sign-in or Sign-up to this endpoint to obtain a new `access_token` (and a new `refresh_token`).\nThe `access_token` is short-lived (≈1h) and must be refreshed before expiration to avoid session expiration.\n\n**Token lifecycle & refresh strategy:**\n- **Access token (`access_token`)**: expires after **3600 seconds (1 hour)**, as indicated by the `expires_in` field in the response.\n- **Refresh token (`refresh_token`)**: valid for **24 hours** but is **single-use** — once consumed by this endpoint it is immediately revoked and a new one is returned.\n- **Recommended refresh cadence**: proactively refresh **5–10 minutes before** the access token expires (i.e. at ~50–55 min after issuance). This avoids race conditions where an in-flight request hits a just-expired token.\n- **Late refresh**: if the access token has already expired but the refresh token is still valid (< 24 h old), calling this endpoint will still succeed and return a fresh pair of tokens.\n- **Expired refresh token**: if the refresh token itself has expired (> 24 h), this endpoint returns `401 Unauthorized`. The user must re-authenticate via Sign-in or Sign-up.\n- **One-time use**: each `refresh_token` can only be used **once**. Always store and use the latest `refresh_token` returned by the most recent call. Replaying an already-consumed refresh token returns `401 Unauthorized`.\n\n**For AI agents / automated integrations:**\nSchedule a token refresh timer based on `expires_in` (e.g. `setTimeout(refresh, (expires_in - 300) * 1000)`). On WebSocket connections, listen for `error_code: 'TOKEN_EXPIRED'` to trigger an immediate refresh.\n\n**Request body parameters:**\n- `refresh_token` (string, required): Refresh token (JWT) issued at sign-in or sign-up\n\n**Response structure (200 OK):**\n```json\n{\n  \"access_token\": \"eyJhbGciOi...\",\n  \"refresh_token\": \"eyJhbGciOi...\",\n  \"token_type\": \"Bearer\",\n  \"expires_in\": 3600,\n  \"issuer\": \"foo.domain\",\n  \"audience\": \"foo.domain\",\n  \"subject\": \"<externalUserId>\",\n  \"roles\": [\"USER\"]\n}\n```","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["refresh_token"],"properties":{"refresh_token":{"type":"string","description":"Refresh token (JWT) issued at sign-in"}}}}},"required":true},"x-examples":{"application/json":{"value":{"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}}},"responses":{"200":{"description":"Default Response"}}}},"/v1.2/auth/iframe":{"post":{"summary":"Get an iframe URL","tags":["Privacy"],"description":"Start a KYC session and get a redirect URL for identity verification.\n\nThe backend resolves the user context from the JWT token, optionally enriches the request with Monerium data, and proxies to the upstream KYC provider.\n\n**Body (optional):**\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `language` | string | UI language code (e.g. `\"en\"`, `\"fr\"`) |\n| `requireSmsVerification` | boolean | If `true`, SMS verification of phone number will be required before KYC/KYB submission |\n\n**Response (200):**\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `chatbotURL` | string | Base URL of the KYC provider |\n| `sessionId` | string | Session identifier |\n| `chatbotFullURL` | string | Full redirect URL with session |\n| `alreadySent` | boolean | `true` if KYC was already submitted |\n\n**Frontend usage:**\n\nCompose a redirect URL: \\`${chatbotURL}?session=${sessionId}&returnUrl=${encodeURIComponent(appUrl)}\\`","security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/auth/enroll":{"post":{"summary":"KYB enrollment","tags":["Privacy"],"description":"Enroll a business (KYB) for identity verification. The backend resolves the user from the JWT, then proxies the request to the upstream KYB enrollment service.\n\nA session URL (`chatbotFullURL`) is returned so the client can upload ID documents directly to the KYB service via `POST /ky/enroll/idDocument` (Bearer session token).\n\nRepresentative data (firstName, lastName, birthDate, birthCity) is automatically retrieved from Pappers using the SIREN number.\n\n## Response statuses\n\n| Status | Description |\n|--------|-------------|\n| `pending_submit` | Enrollment created, documents not yet submitted |\n| `pending_id_document` | No documents yet — redirect user to `chatbotFullURL` |","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["email","companyRegistrationNumber"],"properties":{"email":{"type":"string","format":"email","description":"Contact email (no verification)"},"companyRegistrationNumber":{"type":"string","pattern":"^[0-9]{9}$","description":"SIREN number (9 digits)"},"returnUrl":{"type":"string","format":"uri","description":"Redirect URL after document upload via web form"}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string"},"status":{"type":"string","enum":["pending_submit","pending_id_document"]},"sessionId":{"type":"string"},"chatbotURL":{"type":"string"},"chatbotFullURL":{"type":"string"}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number"},"error":{"type":"string"},"message":{"type":"string"}}}}}}}}},"/v1.2/recovery/status/{safeAddress}":{"get":{"summary":"Get recovery status with optional RPC contract verification (v1.2)","tags":["Blockchain"],"description":"Retrieve the recovery status of a Safe. By default, uses database state (like v1/v1.1). Use `?verifyOnChain=true` to force direct on-chain verification via RPC.\n\n**Modes:**\n- **Database mode (default)**: Fast, uses database state only (same as v1/v1.1)\n- **RPC mode** (`?verifyOnChain=true`): Direct contract calls via RPC for on-chain verification\n\n**Request example (database mode):**\n```\nGET /v1.2/recovery/status/0xd676c6188195372EC269E9C2cAf815C56436A679\nAuthorization: Bearer eyJhbGciOi...\n```\n\n**Request example (RPC mode):**\n```\nGET /v1.2/recovery/status/0xd676c6188195372EC269E9C2cAf815C56436A679?verifyOnChain=true\nAuthorization: Bearer eyJhbGciOi...\n```","parameters":[{"schema":{"type":"number"},"in":"query","name":"chainId","required":false,"description":"Blockchain ID (optional, defaults to X-Blockchain-Id header or DEFAULT_CHAIN_ID)"},{"schema":{"type":"boolean"},"in":"query","name":"verifyOnChain","required":false,"description":"If true, forces direct RPC contract verification. If false or omitted, uses database only (default behavior)"},{"schema":{"type":"string"},"in":"path","name":"safeAddress","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/iban/transactions":{"get":{"summary":"List IBAN transactions (placeholder)","tags":["Blockchain"],"description":"Returns transactions associated with a Safe IBAN. Not yet implemented.","parameters":[{"schema":{"type":"string"},"in":"query","name":"safeAddress","required":true},{"schema":{"type":"number"},"in":"query","name":"chainId","required":false}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/iban/balances":{"get":{"summary":"Get IBAN account balance (placeholder)","tags":["Blockchain"],"description":"Returns the balance of the account associated with a Safe IBAN. Not yet implemented.","parameters":[{"schema":{"type":"string"},"in":"query","name":"safeAddress","required":true},{"schema":{"type":"number"},"in":"query","name":"chainId","required":false}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/iban/revoke":{"post":{"summary":"Revoke an IBAN (placeholder)","tags":["Blockchain"],"description":"Request cancellation of an IBAN associated with a Safe. Not yet implemented.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["safeAddress"],"properties":{"safeAddress":{"type":"string"},"chainId":{"type":"number"}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/sepa/iban/add":{"post":{"summary":"Create a SEPA IBAN (direct or passkey-gated)","tags":["Blockchain"],"description":"Creates a new IBAN for the authenticated user. When the domain flag `isSepaIbanAddWebauthnEnabled` is enabled, this route returns a challenge and requires `PUT /v1.2/sepa/iban/add`. When disabled, this route executes IBAN creation directly.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["holderName"],"properties":{"holderName":{"type":"string","minLength":1},"safeAddress":{"type":"string"},"blockchainId":{"type":"number"},"label":{"type":"string","maxLength":100,"pattern":"^[a-zA-Z0-9 ._-]*$"}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}},"put":{"summary":"Confirm and execute a passkey-gated SEPA IBAN creation","tags":["Blockchain"],"description":"Confirms an IBAN creation intent by verifying a WebAuthn assertion, then executes IBEXSAFE `POST /iban/add`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["approvalId","credential"],"properties":{"approvalId":{"type":"string"},"credential":{"type":"object","additionalProperties":true}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/sepa/iban":{"get":{"summary":"List the authenticated user's SEPA IBANs","tags":["Blockchain"],"description":"Reads all `sepa.iban.*` entries from IBEXSAFE for the authenticated user.","security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/sepa/iban/modify":{"patch":{"summary":"Modify the label of an existing IBAN","tags":["Blockchain"],"description":"Updates the `label` of an IBAN owned by the authenticated user via IBEXSAFE (`PATCH /iban/modify`). Pass an empty string to clear the label.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["iban","label"],"properties":{"iban":{"type":"string","minLength":1},"label":{"type":"string","maxLength":100,"pattern":"^[a-zA-Z0-9 ._-]*$"}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/sepa/payments":{"post":{"summary":"Initiate a SEPA / SEPA Instant payment approval","tags":["Blockchain"],"description":"Creates a passkey-gated payment approval intent. The debtor IBAN must belong to the authenticated user (i.e. previously created via the `POST` + `PUT` `/v1.2/sepa/iban/add` flow). The request is executed only after `PUT /v1.2/sepa/payments` confirms the passkey credential.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["reference","channel","amount","currency","debtor","creditor"],"properties":{"reference":{"type":"string"},"channel":{"type":"string","enum":["SEPA","SEPAINSTANT"]},"amount":{"type":"string"},"currency":{"type":"string","enum":["EUR"]},"source":{"type":"string"},"sourcePayment":{"type":"string"},"remittanceInfo":{"type":"string"},"debtor":{"type":"object","required":["name","iban"],"properties":{"name":{"type":"string"},"iban":{"type":"string"}}},"creditor":{"type":"object","required":["name","iban"],"properties":{"name":{"type":"string"},"iban":{"type":"string"}}}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}},"put":{"summary":"Confirm and execute a passkey-gated SEPA payment","tags":["Blockchain"],"description":"Confirms a payment approval intent by verifying a WebAuthn assertion, then executes IBEx SEPA `POST /payments`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["approvalId","credential"],"properties":{"approvalId":{"type":"string"},"credential":{"type":"object","additionalProperties":true}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/sepa/transactions":{"get":{"summary":"List SEPA transactions for the authenticated user","tags":["Blockchain"],"description":"Returns the SEPA transactions that touch any IBAN owned by the authenticated user (sender or beneficiary). When `iban` query param is provided, the IBAN must belong to the user. When omitted, the endpoint fans out to IBEx SEPA `GET /transactions?iban=<X>` for each user IBAN, merges the results, sorts by `createdAt DESC` and paginates in memory.","parameters":[{"schema":{"type":"string"},"in":"query","name":"iban","required":false},{"schema":{"type":"string","enum":["SEPA_IN","SEPA_OUT"]},"in":"query","name":"type","required":false},{"schema":{"type":"string","enum":["ask","pending","completed","cancelled","failed","rejected"]},"in":"query","name":"status","required":false},{"schema":{"type":"string"},"in":"query","name":"statusCode","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false},{"schema":{"type":"number","minimum":1},"in":"query","name":"page","required":false},{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"limit","required":false}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/sepa/transactions/{id}":{"get":{"summary":"Get a single SEPA transaction","tags":["Blockchain"],"description":"Returns the full SEPA transaction (including the linked SEPA message). Returns 404 if the transaction does not involve any IBAN owned by the authenticated user, in order to avoid enumeration.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/sepa/mandates":{"post":{"summary":"Create a SEPA mandate and push it to IBEx SEPA","tags":["Blockchain"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["sourceIban","destinationIban","percent","trigger","signature"],"properties":{"sourceIban":{"type":"string"},"destinationIban":{"type":"string"},"destinationName":{"type":"string"},"destinationBic":{"type":"string"},"percent":{"type":"number","minimum":0,"maximum":100},"trigger":{"type":"object","required":["mode"],"properties":{"mode":{"type":"string","enum":["all","whitelist"]},"whitelistRules":{"type":"array","items":{"type":"object","required":["kind"],"properties":{"kind":{"type":"string"},"operator":{"type":"string"},"value":{"type":"string"},"values":{"type":"array","items":{"type":"string"}},"minAmount":{"type":"string"},"maxAmount":{"type":"string"},"currency":{"type":"string"},"meta":{"type":"object","additionalProperties":true}},"additionalProperties":true}}}},"signature":{"type":"object","required":["message","signature"],"properties":{"message":{"type":"string","minLength":1},"signature":{"type":"string","minLength":1},"safeOperationUserOpHash":{"type":"string"},"signatureCapturedAt":{"type":"string"}}}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}},"get":{"summary":"List SEPA mandates for authenticated user","tags":["Blockchain"],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/sepa/mandates/{id}":{"get":{"summary":"Get one SEPA mandate","tags":["Blockchain"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/sepa/mandates/{id}/status":{"patch":{"summary":"Update a SEPA mandate status","tags":["Blockchain"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["validated","suspended","cancelled"]}}}}},"required":true},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/sepa/mandates/{id}/cancel":{"post":{"summary":"Cancel a SEPA mandate","tags":["Blockchain"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/chains/":{"get":{"summary":"List active chains, icons, and their modules","tags":["Blockchain"],"description":"Returns all active blockchain networks with their icon URL, explorer URLs and enabled modules (billing, cowswap, recovery, automation, bridge, routeEngine). Chains are sorted by ID.","security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"number","example":100,"description":"Internal chain ID"},"name":{"type":"string","example":"Gnosis","description":"Human-readable chain name"},"icon":{"type":"string","nullable":true,"example":"https://passkeys-prat1.ibex.fi/images/gnosis-chain-logo.png","description":"Absolute URL of the chain icon"},"explorerAddress":{"type":"string","nullable":true,"example":"https://gnosisscan.io/address/","description":"Blockchain explorer base URL for addresses"},"explorerTx":{"type":"string","nullable":true,"example":"https://gnosisscan.io/tx/","description":"Blockchain explorer base URL for transactions"},"modules":{"type":"object","description":"Enabled modules on this chain","properties":{"billing":{"type":"boolean","example":true,"description":"On-chain billing is configured"},"cowswap":{"type":"boolean","example":true,"description":"CowSwap DEX integration is available"},"recovery":{"type":"boolean","example":false,"description":"Social recovery module is deployed"},"automation":{"type":"boolean","example":false,"description":"Allowance / automation module is deployed"},"bridge":{"type":"boolean","example":true,"description":"Cross-chain bridge routing is available"},"routeEngine":{"type":"boolean","example":true,"description":"Unified route engine endpoint is enabled"}}}}}}}}}}}},"/v1.2/safes/{safeAddress}/automation-module/config":{"put":{"summary":"Update automation module configuration for a Safe","tags":["Blockchain"],"description":"Update off-chain automation policy for a Safe.\n\nSupports two transfer modes:\n- `PERCENT_OF_RECEIVED`: transfer a percentage of each received amount.\n- `FIXED_AMOUNT`: transfer a fixed amount each trigger.\n\nYou can additionally configure:\n- `minIntervalMinutes`: minimum delay between two automatic transfers.\n- rolling period cap via `periodCapAmount` + (`periodCap` or `periodCapMinutes`).\n\nValidation rules:\n- `percentage` requires `mode=PERCENT_OF_RECEIVED`.\n- `fixedAmount` and `maxWalletPercentage` require `mode=FIXED_AMOUNT`.\n- Use either `periodCap` or `periodCapMinutes`, not both.\n- `periodCap`/`periodCapMinutes` require `periodCapAmount`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"targetAddress":{"type":"string"},"mode":{"type":"string","enum":["PERCENT_OF_RECEIVED","FIXED_AMOUNT"],"description":"Transfer mode"},"percentage":{"type":"number","minimum":0,"maximum":100,"description":"Used only with mode=PERCENT_OF_RECEIVED"},"fixedAmount":{"type":["string","null"],"description":"Used only with mode=FIXED_AMOUNT (human-readable token units, e.g. \"10.5\"). Use null to clear."},"maxWalletPercentage":{"type":"number","minimum":0,"maximum":100,"description":"Optional cap (wallet %) applied at transfer time when fixedAmount mode is used"},"minIntervalMinutes":{"type":"number","minimum":0,"description":"Minimum delay between 2 automatic transfers (minutes)"},"periodCapAmount":{"type":["string","null"],"description":"Maximum cumulative amount per period (human-readable token units). Use null to clear period cap."},"periodCap":{"type":["string","null"],"enum":["HOURLY","DAILY","WEEKLY","MONTHLY",null],"description":"Predefined period for periodCapAmount"},"periodCapMinutes":{"type":["number","null"],"minimum":1,"description":"Custom period (minutes) for periodCapAmount. Mutually exclusive with periodCap."},"frequency":{"type":"string","enum":["DAILY","WEEKLY","90_DAYS","NONE"]},"tokenAddress":{"type":"string"},"enabled":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"safeAddress","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/swap/quote":{"get":{"summary":"Get swap quote from COWSWAP, 1INCH or both (uses your first Safe as receiver)","tags":["Blockchain"],"parameters":[{"schema":{"type":"string"},"in":"query","name":"sellTokenAddress","required":true},{"schema":{"type":"string"},"in":"query","name":"buyTokenAddress","required":true},{"schema":{"type":"string"},"in":"query","name":"amount","required":true},{"schema":{"type":"number"},"in":"query","name":"chainId","required":false},{"schema":{"type":"string"},"in":"query","name":"safeAddress","required":false},{"schema":{"type":"string","enum":["COWSWAP","1INCH","BOTH"]},"in":"query","name":"provider","required":false}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/routes/capabilities":{"get":{"summary":"Get route capabilities (same-chain swap vs cross-chain bridge)","tags":["Blockchain"],"parameters":[{"schema":{"type":"number"},"in":"query","name":"sourceChainId","required":true},{"schema":{"type":"number"},"in":"query","name":"destinationChainId","required":true},{"schema":{"type":"string","enum":["COWSWAP","1INCH","BRIDGE","BOTH","ALL"]},"in":"query","name":"providerPreference","required":false}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/routes/quote":{"post":{"summary":"Get best route quote across same-chain swap and cross-chain bridge","tags":["Blockchain"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["sourceChainId","destinationChainId","sellTokenAddress","buyTokenAddress","amount"],"properties":{"sourceChainId":{"type":"number"},"destinationChainId":{"type":"number"},"sellTokenAddress":{"type":"string"},"buyTokenAddress":{"type":"string"},"amount":{"type":"string"},"safeAddress":{"type":"string"},"providerPreference":{"type":"string","enum":["COWSWAP","1INCH","BRIDGE","BOTH","ALL"]}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/routes/{routeId}/status":{"get":{"summary":"Get route execution status","tags":["Blockchain"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"routeId","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/{safeAddress}/wallets":{"post":{"summary":"Derive new per-Safe EOA addresses","tags":["Blockchain"],"description":"Derives one or more new EOA addresses for the given Safe of the authenticated signer (`sid` claim).\n\nBody: `{ \"add\": [ { \"family\": \"EVM\", \"count\": 2 }, { \"family\": \"SOLANA\" } ] }`. Each item appends `count` new indices (default 1, max 10) to the per-Safe list of that family. Hard cap per family per Safe: 100.\n\nDerivation is fully deterministic from the passkey master (HKDF info `{family}:safe:{safeAddressLower}:i:{index}`). The master is unsealed server-side from `Signer.data.derivation.sealedMaster` — **no WebAuthn ceremony is required**. If the signer has no sealed master (e.g. legacy / wallet-signin), the call returns `409 NO_MASTER`.\n\nRate limit: ~5s per signer.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["add"],"properties":{"add":{"type":"array","minItems":1,"items":{"type":"object","required":["family"],"properties":{"family":{"type":"string","enum":["EVM","SOLANA","BITCOIN_P2WPKH","BITCOIN_P2TR","BITCOIN_P2WPKH_TESTNET","BITCOIN_P2TR_TESTNET","COSMOS","POLKADOT","TEZOS_TZ1","TEZOS_TZ2","TEZOS_TZ3","NEAR","STELLAR","CARDANO"]},"count":{"type":"integer","minimum":1,"maximum":10}}}}}}}},"required":true},"parameters":[{"schema":{"type":"string"},"in":"path","name":"safeAddress","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"safeAddress":{"type":"string"},"added":{"type":"array","items":{"type":"object","properties":{"family":{"type":"string"},"index":{"type":"integer"},"address":{"type":"string"}}}},"perSafe":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}}}}}}},"/v1.2/safes/vaults":{"get":{"summary":"List DeFi lending pools and vaults","tags":["Blockchain"],"description":"Returns the catalog of active DeFi lending pools/vaults (AAVE, MORPHO, HYPERLIQUID). Use the `provider` query parameter to filter by protocol. When `X-Blockchain-Id` header is provided, returns only that chain; omit it to get all chains.","parameters":[{"schema":{"type":"string","enum":["AAVE","MORPHO","HYPERLIQUID"]},"in":"query","name":"provider","required":false,"description":"Filter by DeFi provider"}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"number"},"blockchainId":{"type":"string"},"provider":{"type":"string"},"poolAddress":{"type":"string"},"name":{"type":["null","string"]},"assetTicker":{"type":["null","string"]},"assetAddress":{"type":["null","string"]},"assetDecimals":{"type":["null","number"]},"apy":{"type":["null","number"]},"tvl":{"type":["null","number"]},"isDefault":{"type":"boolean"},"metadata":{"type":["null","object"],"additionalProperties":true},"supplyToken":{"type":["null","object"],"additionalProperties":true},"borrowToken":{"type":["null","object"],"additionalProperties":true},"poolToken":{"type":["null","object"],"additionalProperties":true}}}}}}}}}},"/v1.2/safes/operations/batch-intent":{"post":{"summary":"Batch Safe operations (intent)","tags":["Blockchain"],"description":"Prepare a batch of operations to be executed with a single WebAuthn authentication (4337). Returns WebAuthn `credentialRequestOptions` and a `batchId`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["safeAddress","operations"],"properties":{"safeAddress":{"type":"string"},"chainId":{"type":"number"},"idempotencyKey":{"type":"string"},"walletMode":{"type":"string","enum":["SAFE_4337","EOA_7702"]},"eoaKeySelection":{"type":"object","properties":{"family":{"type":"string","enum":["EVM"]},"index":{"type":"integer","minimum":0},"safeAddress":{"type":"string"}}},"options":{"type":"object","properties":{"atomic":{"type":"boolean"},"autoApprove":{"type":"boolean"},"ttlSec":{"type":"number"},"reorder":{"type":"boolean"}}},"operations":{"type":"array","minItems":1,"items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["TRANSFER_TOKEN"]},"tokenAddress":{"type":"string"},"to":{"type":"string"},"amount":{"type":"string"},"decimals":{"type":"number"}},"required":["type","tokenAddress","to","amount"]},{"type":"object","properties":{"type":{"type":"string","enum":["SIGN_MESSAGE"]},"message":{"type":"string"}},"required":["type","message"]},{"type":"object","properties":{"type":{"type":"string","enum":["ENABLE_RECOVERY"]},"firstName":{"type":"string"},"lastName":{"type":"string"},"birthDate":{"type":"string"},"birthCity":{"type":"string"},"birthCountry":{"type":"string"}},"required":["type","firstName","lastName","birthDate","birthCity","birthCountry"]},{"type":"object","properties":{"type":{"type":"string","enum":["CANCEL_RECOVERY"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["SWAP_FROM_QUOTE"]},"quoteId":{"type":"string"},"orderUid":{"type":"string"}},"required":["type","quoteId"]},{"type":"object","properties":{"type":{"type":"string","enum":["ROUTE_FROM_QUOTE"]},"quoteId":{"type":"string"}},"required":["type","quoteId"]},{"type":"object","properties":{"type":{"type":"string","enum":["AAVE_SUPPLY"]},"amount":{"type":"string"},"assetTicker":{"type":"string"},"tokenAddress":{"type":"string"},"decimals":{"type":"number","minimum":0},"referralCode":{"type":"number","minimum":0,"maximum":65535},"poolAddress":{"type":"string"}},"required":["type","amount"]},{"type":"object","properties":{"type":{"type":"string","enum":["AAVE_WITHDRAW"]},"amount":{"type":"string"},"assetTicker":{"type":"string"},"tokenAddress":{"type":"string"},"decimals":{"type":"number","minimum":0},"poolAddress":{"type":"string"}},"required":["type","amount"]},{"type":"object","properties":{"type":{"type":"string","enum":["MORPHO_SUPPLY"]},"amount":{"type":"string"},"assetTicker":{"type":"string"},"tokenAddress":{"type":"string"},"decimals":{"type":"number","minimum":0},"vaultAddress":{"type":"string"}},"required":["type","amount"]},{"type":"object","properties":{"type":{"type":"string","enum":["MORPHO_WITHDRAW"]},"shares":{"type":"string"},"amount":{"type":"string"},"assetTicker":{"type":"string"},"tokenAddress":{"type":"string"},"decimals":{"type":"number","minimum":0},"vaultAddress":{"type":"string"}},"required":["type"]},{"type":"object","required":["type","owner","threshold"],"properties":{"type":{"type":"string","enum":["ADD_OWNER"]},"owner":{"$ref":"#/components/schemas/def-0"},"threshold":{"type":"number","minimum":1}}},{"type":"object","required":["type","owner","threshold"],"properties":{"type":{"type":"string","enum":["REMOVE_OWNER"]},"owner":{"$ref":"#/components/schemas/def-0"},"threshold":{"type":"number","minimum":1}}},{"type":"object","required":["type","threshold"],"properties":{"type":{"type":"string","enum":["CHANGE_THRESHOLD"]},"threshold":{"type":"number","minimum":1}}},{"type":"object","required":["type","targetAddress","percentage","frequency","tokenAddress"],"properties":{"type":{"type":"string","enum":["ENABLE_AUTOMATION_MODULE"]},"targetAddress":{"type":"string"},"percentage":{"type":"number","minimum":0,"maximum":100},"frequency":{"type":"string"},"tokenAddress":{"type":"string"},"workerAddress":{"type":"string"}}}]}}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/operations/batch-execute":{"put":{"summary":"Batch Safe operations (execution)","tags":["Blockchain"],"description":"Execute a batch of operations previously prepared with `batch-intent`. Requires the signed WebAuthn credential.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["batchId","credential"],"properties":{"batchId":{"type":"string"},"credential":{"type":"object"}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/operations/batch/{batchId}/status":{"get":{"summary":"Get batch status","tags":["Blockchain"],"description":"GET /v1.2/safes/operations/batch/:batchId/status provides the current server-side status of a multioperation batch.","parameters":[{"schema":{"type":"string"},"in":"path","name":"batchId","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/operations/{userOpHash}/status":{"get":{"summary":"Get operation status by userOpHash","tags":["Blockchain"],"description":"GET /v1.2/safes/operations/:userOpHash/status permet de vérifier l'état d'une opération Safe via son userOpHash.","parameters":[{"schema":{"type":"string"},"in":"path","name":"userOpHash","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/operations":{"post":{"summary":"Prepare Safe operations","tags":["Blockchain"],"description":"Prepare one or more Safe operations. Returns WebAuthn `credentialRequestOptions` for the next step (PUT on the same route).","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["safeAddress","operations"],"properties":{"safeAddress":{"type":"string"},"chainId":{"type":"number"},"signerId":{"type":"string"},"walletMode":{"type":"string","enum":["SAFE_4337","EOA_7702"]},"eoaKeySelection":{"type":"object","properties":{"family":{"type":"string","enum":["EVM"]},"index":{"type":"integer","minimum":0},"safeAddress":{"type":"string"}}},"operations":{"type":"array","minItems":1,"items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["TRANSFER_TOKEN"]},"tokenAddress":{"type":"string"},"to":{"type":"string"},"amount":{"type":"string"},"decimals":{"type":"number"}},"required":["type","tokenAddress","to","amount"]},{"type":"object","properties":{"type":{"type":"string","enum":["SIGN_MESSAGE"]},"message":{"type":"string"}},"required":["type","message"]},{"type":"object","properties":{"type":{"type":"string","enum":["ENABLE_RECOVERY"]},"firstName":{"type":"string"},"lastName":{"type":"string"},"birthDate":{"type":"string"},"birthCity":{"type":"string"},"birthCountry":{"type":"string"}},"required":["type","firstName","lastName","birthDate","birthCity","birthCountry"]},{"type":"object","properties":{"type":{"type":"string","enum":["CANCEL_RECOVERY"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["SWAP_FROM_QUOTE"]},"quoteId":{"type":"string"},"orderUid":{"type":"string"}},"required":["type","quoteId"]},{"type":"object","properties":{"type":{"type":"string","enum":["ROUTE_FROM_QUOTE"]},"quoteId":{"type":"string"}},"required":["type","quoteId"]},{"type":"object","properties":{"type":{"type":"string","enum":["AAVE_SUPPLY"]},"amount":{"type":"string"},"assetTicker":{"type":"string"},"tokenAddress":{"type":"string"},"decimals":{"type":"number","minimum":0},"referralCode":{"type":"number","minimum":0,"maximum":65535},"poolAddress":{"type":"string"}},"required":["type","amount"]},{"type":"object","properties":{"type":{"type":"string","enum":["AAVE_WITHDRAW"]},"amount":{"type":"string"},"assetTicker":{"type":"string"},"tokenAddress":{"type":"string"},"decimals":{"type":"number","minimum":0},"poolAddress":{"type":"string"}},"required":["type","amount"]},{"type":"object","properties":{"type":{"type":"string","enum":["MORPHO_SUPPLY"]},"amount":{"type":"string"},"assetTicker":{"type":"string"},"tokenAddress":{"type":"string"},"decimals":{"type":"number","minimum":0},"vaultAddress":{"type":"string"}},"required":["type","amount"]},{"type":"object","properties":{"type":{"type":"string","enum":["MORPHO_WITHDRAW"]},"shares":{"type":"string"},"amount":{"type":"string"},"assetTicker":{"type":"string"},"tokenAddress":{"type":"string"},"decimals":{"type":"number","minimum":0},"vaultAddress":{"type":"string"}},"required":["type"]},{"type":"object","required":["type","owner","threshold"],"properties":{"type":{"type":"string","enum":["ADD_OWNER"]},"owner":{"$ref":"#/components/schemas/def-0"},"threshold":{"type":"number","minimum":1}}},{"type":"object","required":["type","owner","threshold"],"properties":{"type":{"type":"string","enum":["REMOVE_OWNER"]},"owner":{"$ref":"#/components/schemas/def-0"},"threshold":{"type":"number","minimum":1}}},{"type":"object","required":["type","threshold"],"properties":{"type":{"type":"string","enum":["CHANGE_THRESHOLD"]},"threshold":{"type":"number","minimum":1}}},{"type":"object","required":["type","targetAddress","percentage","frequency","tokenAddress"],"properties":{"type":{"type":"string","enum":["ENABLE_AUTOMATION_MODULE"]},"targetAddress":{"type":"string"},"percentage":{"type":"number","minimum":0,"maximum":100},"frequency":{"type":"string"},"tokenAddress":{"type":"string"},"workerAddress":{"type":"string"}}}]}}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}},"put":{"summary":"Submit Safe operations","tags":["Blockchain"],"description":"The passkey `credential` is obtained by using [simplewebauthn](https://simplewebauthn.dev/docs/packages/browser):<br>`const credential = await startAuthentication({optionsJSON: credentialRequestOptions});`<br>","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["credential"],"properties":{"credential":{"type":"object"}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"userOpHash":{"type":"string"},"success":{"type":"boolean"},"signature":{"type":"string"},"chainId":{"type":"number"},"type":{"type":"string"}},"additionalProperties":true}}}}}}},"/v1.2/safes/bitcoin/info":{"get":{"summary":"Bitcoin network info","tags":["WIP"],"description":"Proxy `getblockchaininfo` via WEB3_URL /btc/rpc.","parameters":[{"schema":{"type":"string","enum":["mainnet","testnet"]},"in":"query","name":"network","required":false}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/bitcoin/fees":{"get":{"summary":"Bitcoin fee rates (sat/vB)","tags":["WIP"],"description":"Estimates simple fee tiers using `estimatesmartfee` via WEB3_URL /btc/rpc.","parameters":[{"schema":{"type":"string","enum":["mainnet","testnet"]},"in":"query","name":"network","required":false}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/bitcoin/utxos":{"get":{"summary":"List UTXOs for an address","tags":["WIP"],"description":"Uses Core `scantxoutset` with a descriptor `addr(<address>)` when enabled on the node.","parameters":[{"schema":{"type":"string"},"in":"query","name":"address","required":true},{"schema":{"type":"string","enum":["mainnet","testnet"]},"in":"query","name":"network","required":false}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/bitcoin/psbt/build":{"post":{"summary":"Build PSBT (placeholder)","tags":["WIP"],"description":"Returns 501 for now; PSBT building client-side is recommended. Server-side builder can be added later.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"inputs":{"type":"array","items":{"type":"object"}},"outputs":{"type":"array","items":{"type":"object"}},"changeAddress":{"type":"string"},"sendAll":{"type":"boolean"},"feeProfile":{"type":"string","enum":["slow","standard","fast"]},"network":{"type":"string","enum":["mainnet","testnet"]}}}}}},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/bitcoin/tx/broadcast":{"post":{"summary":"Broadcast signed raw transaction (hex)","tags":["WIP"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["rawtx"],"properties":{"rawtx":{"type":"string"},"network":{"type":"string","enum":["mainnet","testnet"]}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/safes/bitcoin/send/prepare":{"post":{"summary":"Prepare a simple Bitcoin send (no signing)","tags":["WIP"],"description":"Select UTXOs, estimate fee, compute change. Returns a structure the client can turn into a PSBT.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["from","to"],"properties":{"from":{"type":"string"},"to":{"type":"string"},"amountSat":{"type":"number"},"sendAll":{"type":"boolean"},"feeProfile":{"type":"string","enum":["slow","standard","fast"],"default":"standard"},"network":{"type":"string","enum":["mainnet","testnet"]},"externalFeeSponsor":{"type":"boolean","description":"If true, do not require sender UTXOs to cover fees (collaborative PSBT). Sender covers only amount."}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/users/me":{"get":{"summary":"User's aggregated dashboard","tags":["Privacy"],"description":"Proxy aggregator that calls the user-facing sub-endpoints in parallel and returns a compact payload with section data at the top-level keys.\n\n**Aggregated sections (7):**\n- `addresses` — `GET /v1.2/users/me/address`\n- `balances` — `GET /v1.2/users/me/balances`\n- `ibans` — `GET /v1.2/users/me/ibans`\n- `signers` — `GET /v1.2/users/me/signers`\n- `transactions` — `GET /v1.2/users/me/transactions`\n- `kycStatus` — `GET /v1.2/users/kyc/status`\n- `addressbook` — `GET /v1.2/users/me/addressbook`\n\n**Not included in the aggregator** (call separately):\n- `GET /v1.2/users/me/lending` — DeFi lending catalog (add `?userScoped=true` for user-scoped entries)\n\nThis endpoint is slower (external on-chain queries) and is excluded to keep the aggregator fast. Call it individually when needed.\n\nWhen one or more sections fail, an optional `errors` object is added (per section: `{ status, message }`).","security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"post":{"summary":"Write authenticated user's data","tags":["Privacy"],"description":"Store arbitrary key/value data for the authenticated user. The user identity is taken from the JWT — any identifier passed in the body is ignored. Subsequent calls to `GET /v1.2/users/me` return the public keys inside the `userdata` object.\n\n**Privacy convention:**\n- Keys prefixed with `private.` are stored but never returned by GET (write-only sensitive data, e.g. an internal API key or a tier label).\n- All other keys are **public** for the authenticated user — they are returned by `GET /v1.2/users/me`.\n\n**Request example:**\n```json\n{\n  \"path\": \"/v1.2/users/me\",\n  \"method\": \"POST\",\n  \"headers\": { \"Authorization\": \"Bearer eyJhbGciOi...\" },\n  \"body\": {\n    \"data\": {\n      \"email\": \"jane.doe@foo.domain\",\n      \"firstName\": \"Jane\",\n      \"lastName\": \"Doe\",\n      \"language\": \"en\",\n      \"optin.newsletter\": true,\n      \"private.apiKey\": \"sk_live_xxx\"\n    }\n  }\n}\n```\n\n**Response example (200):**\n```json\n{ \"success\": true }\n```","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","additionalProperties":true,"description":"Flat key/value map. Keys prefixed with `private.` are stored but never returned by GET."}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"true when the data was successfully written."}}}}}}}}},"/v1.2/users/me/operations":{"get":{"summary":"Get user operations","tags":["Blockchain"],"description":"Returns all operations related to the user's wallet(s). Use `?refresh=true` to also fetch onchain contract events (EnabledModule, AddedOwner, etc.) directly from the RPC.","parameters":[{"schema":{"type":"boolean"},"in":"query","name":"refresh","required":false,"description":"When true, also fetch onchain Safe events from RPC (EnabledModule, DisabledModule, AddedOwner, RemovedOwner, ChangedThreshold, ExecutionSuccess, ExecutionFailure)"}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/users/me/chainid":{"get":{"summary":"Active chains and wallets","tags":["Blockchain"],"description":"Returns all active chains for the platform, enriched with the authenticated signer's wallets on each chain.\n\nThe response is structured hierarchically: **chain → wallets** and includes per-chain module status.\n\nIf the signer has no wallet on a chain, that chain is still returned with `wallets: []`.\n\nUse `GET /v1.2/users/me/balances` separately to fetch token balances.\n\n**Signer filtering:** uses the JWT `sid` claim to return only the authenticated signer's wallets (falls back to all signers for legacy tokens without `sid`).\n\n**Modules semantics:**\n- `recovery`: `\"N/A\"` when recovery is not available on this chain (`recoveryContract` and `recoveryModuleMasterAddress` are both missing), otherwise boolean activation for this signer.\n- `automation`: `\"N/A\"` when automation is not available on this chain (`automationModuleAddress` missing), otherwise boolean activation for this signer.\n- `multisig`: `true` when Safe multisig is available on this chain (`isSafeWallet=true`), otherwise `\"N/A\"`.\n\n**Response structure:**\n```json\n{\n  \"defaultChainId\": 421614,\n  \"chains\": [\n    {\n      \"chainId\": 421614,\n      \"chainName\": \"Arbitrum Testnet Sepolia\",\n      \"modules\": {\n        \"recovery\": \"N/A\",\n        \"automation\": true,\n        \"multisig\": true\n      },\n      \"wallets\": [\n        { \"address\": \"0x490E...cAC2\" }\n      ]\n    },\n    {\n      \"chainId\": 100,\n      \"chainName\": \"Gnosis\",\n      \"modules\": {\n        \"recovery\": false,\n        \"automation\": true,\n        \"multisig\": true\n      },\n      \"wallets\": []\n    }\n  ]\n}\n```","security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"defaultChainId":{"type":"number"},"chains":{"type":"array","items":{"type":"object","properties":{"chainId":{"type":"number"},"chainName":{"type":"string"},"modules":{"type":"object","description":"Module status on this chain for the signer.","properties":{"recovery":{"oneOf":[{"type":"boolean"},{"type":"string","enum":["N/A"]}]},"automation":{"oneOf":[{"type":"boolean"},{"type":"string","enum":["N/A"]}]},"multisig":{"oneOf":[{"type":"boolean","enum":[true]},{"type":"string","enum":["N/A"]}]}}},"wallets":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"}}}}}}}}}}}}}}},"/v1.2/users/me/address":{"get":{"summary":"User's wallet addresses","tags":["Blockchain"],"description":"Return wallet addresses for the signer that authenticated the current JWT session, across all deployed chains, with Safe module status per chain.\n\n**Signer filtering (`sid` JWT claim):**\n- The JWT contains a `sid` claim identifying the signer (passkey / EOA) used to authenticate.\n- Only safes belonging to **that signer** are returned — not all signers of the user.\n- If the JWT was issued before `sid` support (no signer claim), the endpoint falls back to returning all signers' safes.\n\n**Multi-chain support:**\n- Each safe address can be deployed on multiple chains. The `chainIds` array lists every chain ID, sorted numerically.\n- The `chains` array provides per-chain details including activated **Safe modules** (recovery, automation/allowance).\n\n**Optional derived wallet enrichment (`includeDerived=true`):**\n- Adds `wallets[].derived.perSafe`: per-family derived addresses for this Safe.\n- Adds `wallets[].derived.global.eoaAddresses`: signer-global derived addresses (without `WALLET_TYPE_SIGNIN` filtering).\n\n**Modules:**\n- `recovery` — Social recovery module (guardians can recover the wallet).\n- `automation` — Allowance/automation module (scheduled transfers, DCA, etc.).\n- Modules are only included in the response when they are activated on that chain.\n\n**Response example (200):**\n```json\n{\n  \"rpId\": \"ibex.fi\",\n  \"signerId\": \"AVZs0qRCBSmfThZWu37g...\",\n  \"count\": 1,\n  \"wallets\": [\n    {\n      \"safeAddress\": \"0xd676c6...A679\",\n      \"chainIds\": [100, 421614],\n      \"threshold\": 1,\n      \"chains\": [\n        { \"chainId\": 100 },\n        {\n          \"chainId\": 421614,\n          \"modules\": {\n            \"recovery\": { \"enabled\": true, \"moduleAddress\": \"0xRecov...\" },\n            \"automation\": {\n              \"enabled\": true,\n              \"moduleAddress\": \"0xAllow...\",\n              \"targetAddress\": \"0xDest...\",\n              \"percentage\": 25,\n              \"frequency\": \"DAILY\",\n              \"tokenAddress\": \"0xUSDC...\"\n            }\n          }\n        }\n      ],\n      \"createdAt\": \"2025-01-15T10:30:00.000Z\",\n      \"updatedAt\": \"2025-03-20T14:00:00.000Z\",\n      \"primary\": true\n    }\n  ]\n}\n```","parameters":[{"schema":{"type":"boolean"},"in":"query","name":"includeDerived","required":false,"description":"When true, adds per-safe and signer-global derived wallet addresses."}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"rpId":{"type":"string"},"externalUserId":{"type":"string"},"signerId":{"type":"string","description":"The signer ID from the JWT (if available)"},"count":{"type":"number"},"wallets":{"type":"array","items":{"type":"object","properties":{"safeAddress":{"type":"string"},"chainIds":{"type":"array","items":{"type":"number"},"description":"All chain IDs this safe is deployed on, sorted numerically"},"threshold":{"type":"number","description":"Safe multisig threshold"},"chains":{"type":"array","description":"Per-chain details with activated Safe modules","items":{"type":"object","properties":{"chainId":{"type":"number"},"modules":{"type":"object","description":"Activated Safe modules on this chain (omitted if none)","properties":{"recovery":{"type":"object","properties":{"enabled":{"type":"boolean"},"moduleAddress":{"type":"string"}}},"automation":{"type":"object","properties":{"enabled":{"type":"boolean"},"moduleAddress":{"type":"string"},"targetAddress":{"type":"string"},"transferMode":{"type":"string"},"percentage":{"type":"number"},"fixedAmount":{"type":"string"},"maxWalletPercentage":{"type":"number"},"minIntervalMinutes":{"type":"number"},"periodCapAmount":{"type":"string"},"periodCapMinutes":{"type":"number"},"frequency":{"type":"string"},"tokenAddress":{"type":"string"}}}}}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"primary":{"type":"boolean"},"derived":{"type":"object","description":"Present when includeDerived=true.","properties":{"perSafe":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"global":{"type":"object","properties":{"eoaAddresses":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"address":{"type":"string"}}}}}}}}}}}}}}}}}}},"/v1.2/users/me/wallets/global":{"post":{"summary":"Derive new global wallet addresses","tags":["Blockchain"],"description":"Derives one or more additional signer-global addresses by family.\n\nThis extends the global derivation set for the authenticated signer (`sid` claim), independently from per-safe derivations.\n\nBody: `{ \"add\": [ { \"family\": \"EVM\", \"count\": 2 }, { \"family\": \"SOLANA\" } ] }`.\nEach item appends `count` new indices (default 1, max 10) for that family.\n\nRequires a signer with `derivation.sealedMaster` (legacy/wallet-signin signers return `409 NO_MASTER`).","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["add"],"properties":{"add":{"type":"array","minItems":1,"items":{"type":"object","required":["family"],"properties":{"family":{"type":"string","enum":["EVM","SOLANA","BITCOIN_P2WPKH","BITCOIN_P2TR","BITCOIN_P2WPKH_TESTNET","BITCOIN_P2TR_TESTNET","COSMOS","POLKADOT","TEZOS_TZ1","TEZOS_TZ2","TEZOS_TZ3","NEAR","STELLAR","CARDANO"]},"count":{"type":"integer","minimum":1,"maximum":10}}}}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"family":{"type":"string"},"index":{"type":"integer"},"address":{"type":"string"}}}},"globalIndexed":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"globalEoaAddresses":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"address":{"type":"string"}}}},"limits":{"type":"object","properties":{"maxIndexPerFamily":{"type":"number"},"maxCountPerRequest":{"type":"number"},"supportedFamilies":{"type":"array","items":{"type":"string"}}}}}}}}}}}},"/v1.2/users/me/ibans":{"get":{"summary":"User's IBANs","tags":["Privacy"],"description":"Return all IBANs associated with the authenticated user's wallet addresses, including their status and blockchain chain.","security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number"},"ibans":{"type":"array","items":{"type":"object","properties":{"iban":{"type":"string"},"formatted":{"type":"string"},"bic":{"type":"string"},"holderName":{"type":"string"},"safeAddress":{"type":"string"},"blockchainId":{"type":"number"},"status":{"type":"string","description":"IBAN status (e.g. pending, active)"},"provider":{"type":"string"},"accountNumber":{"type":"string"},"dateUsed":{"type":"string","format":"date-time"}}}}}}}}}}}},"/v1.2/users/me/signers":{"get":{"summary":"User's signers","tags":["Privacy"],"description":"Return all signers for the authenticated user. Each signer is either a **passkey** (WebAuthn credential) or an **EOA wallet** (Externally Owned Account). The `type` field indicates PASSKEY, EOA, or EMAIL_TOKEN. Passkeys include key name and display name. Each signer may own one or more Safe wallets.","security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number"},"signers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"PASSKEY, EOA, or EMAIL_TOKEN"},"typeDescription":{"type":"string","description":"Human-readable description of the signer type"},"walletMode":{"type":"string","description":"SAFE_4337 or EOA_7702"},"keyName":{"type":"string","nullable":true,"description":"Passkey friendly name (null for EOA)"},"keyDisplayName":{"type":"string","nullable":true,"description":"Passkey display name (null for EOA)"},"createdAt":{"type":"string","format":"date-time"},"safesCount":{"type":"number","description":"Number of Safe wallets owned by this signer"}}}}}}}}}}}},"/v1.2/users/kyc/status":{"get":{"summary":"KYC verification status","tags":["Privacy"],"description":"Return the KYC verification status for the authenticated user. The `level` field indicates the current verification level (e.g. 0 = not started, 1 = pending, 2 = verified).","security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"externalUserId":{"type":"string"},"kycLevel":{"type":"string","description":"KYC level (0=none, 1=pending, 2=verified)"},"status":{"type":"string","description":"Human-readable KYC status"},"verified":{"type":"boolean","description":"Whether the user has completed KYC"}}}}}}}}},"/v1.2/users/me/validate-email":{"post":{"summary":"Send email verification code","tags":["Privacy"],"description":"Send a verification code to the user's email address.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["email","externalUserId"],"properties":{"email":{"type":"string","description":"Email address to validate"},"externalUserId":{"type":"string","description":"External user identifier"}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1.2/users/me/confirm-email":{"post":{"summary":"Confirm email verification code","tags":["Privacy"],"description":"Confirm the verification code sent to the user's email.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["email","code","externalUserId"],"properties":{"email":{"type":"string","description":"Email address being verified"},"code":{"type":"string","description":"Verification code received by email"},"externalUserId":{"type":"string","description":"External user identifier"}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1.2/users/me/validate-sms":{"post":{"summary":"Send SMS verification code","tags":["Privacy"],"description":"Send a 6-digit verification code by SMS to a phone number.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["telephone","externalUserId"],"properties":{"telephone":{"type":"string","description":"Phone number (E.164 or local format)"},"externalUserId":{"type":"string","description":"External user identifier"},"phonePolicy":{"type":"string","description":"Validation policy: \"frMobile\" or \"any\" (default)"}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1.2/users/me/confirm-sms":{"post":{"summary":"Confirm SMS verification code","tags":["Privacy"],"description":"Confirm the SMS verification code sent to the phone number.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["telephone","code","externalUserId"],"properties":{"telephone":{"type":"string","description":"Phone number (same as sent to validate-sms)"},"code":{"type":"string","description":"6-digit verification code received by SMS"},"externalUserId":{"type":"string","description":"External user identifier"},"phonePolicy":{"type":"string","description":"Validation policy: \"frMobile\" or \"any\" (default)"},"persistTelephoneToKyb":{"type":"boolean","description":"If true, persist verified phone into KYB telephone field"}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1.2/users/me/balances":{"get":{"summary":"User's balances","tags":["Privacy"],"description":"Unified balances endpoint for the authenticated user. The API always forwards `ibexid=<externalUserId from JWT>` to BCReader (`externalUserId` / `externaluserid` aliases are equivalent in BCReader).\n\nScopes:\n- Default (no `walletAddress` and no `iban`): all user wallets and IBANs via `/v1.2/balances`.\n- `walletAddress`: scoped to one user-owned wallet via `/v1.2/balances/:identifier`.\n- `iban`: scoped to one IBAN via `/v1.2/balances/:identifier`.\n\nIf both `walletAddress` and `iban` are provided, the request is rejected with `400`.\n\nChain behavior in aggregated mode:\n- No explicit `blockchainId` (neither header nor query): returns balances across all chains.\n- Explicit `blockchainId` (header `X-Blockchain-Id` or query param): returns balances for that chain only.\n\nPagination note:\n- This endpoint may expose pagination fields depending on BCReader mode.\n- No API-level default override is enforced for `page` / `limit` here when omitted; behavior follows BCReader defaults.\n\nFor wallet scope, if BCReader reports the address as not indexed yet, the API triggers indexing and retries once.","parameters":[{"schema":{"type":"string"},"in":"query","name":"walletAddress","required":false,"description":"Optional wallet scope. Must belong to the authenticated user: /users/me/address signers[].safes[].address or signers[].addresses[].address (including derived.global.eoaAddresses)."},{"schema":{"type":"string"},"in":"query","name":"iban","required":false,"description":"Optional IBAN scope. Spaces are allowed in input and will be normalized. Cannot be used with walletAddress."},{"schema":{"type":"string"},"in":"query","name":"blockchainId","required":false,"description":"Optional explicit chain selector for aggregated mode. When omitted, returns balances across all chains. Same effect as X-Blockchain-Id header."},{"schema":{"type":"string","enum":["true","false"],"default":"false"},"in":"query","name":"includeZero","required":false,"description":"Include zero-balance tokens"},{"schema":{"type":"string","enum":["true","false"],"default":"false"},"in":"query","name":"includeEmpty","required":false,"description":"Include wallets and chains with no tokens. When false (default), empty wallets and chains containing only empty wallets are stripped from the response."},{"schema":{"type":"string","enum":["true","false"],"default":"true"},"in":"query","name":"includePrices","required":false,"description":"Attach real-time prices"},{"schema":{"type":"string"},"in":"query","name":"page","required":false,"description":"Page number (pagination)"},{"schema":{"type":"string"},"in":"query","name":"limit","required":false,"description":"Items per page (1–100)"}],"security":[{"JWT":[]}],"responses":{"200":{"description":"BCReader v1.2 payload (scoped or aggregated).","content":{"application/json":{"schema":{"description":"BCReader v1.2 payload (scoped or aggregated).","type":"object","additionalProperties":true}}}}}}},"/v1.2/users/me/transactions":{"get":{"summary":"User's transactions","tags":["Privacy"],"description":"Unified transactions endpoint for the authenticated user.\n\nScopes:\n- `walletAddress`: one user-owned wallet.\n- `iban`: one IBAN.\n- default (no `walletAddress` and no `iban`): aggregated user mode over all user wallets/IBANs.\n\nChain behavior in aggregated mode:\n- explicit `blockchainId` (query or `X-Blockchain-Id`): aggregate only this chain.\n- no explicit chain: aggregate all user chains.\n\nTransaction class filter:\n- `scope=mixed` (default): returns both `crypto` and `fiat` sections.\n- `scope=crypto`: returns only `crypto`.\n- `scope=fiat`: returns only `fiat`.\n\nDefault values when omitted:\n- `startDate=2025-01-01`\n- `endDate=<tomorrow UTC date>`\n- `page=1`\n- `limit=50`\n- `includePrices=true`","parameters":[{"schema":{"type":"string"},"in":"query","name":"walletAddress","required":false,"description":"Optional wallet scope. Must belong to the authenticated user: /users/me/address signers[].safes[].address or signers[].addresses[].address (including derived.global.eoaAddresses)."},{"schema":{"type":"string"},"in":"query","name":"iban","required":false,"description":"Optional IBAN scope. Spaces are allowed in input and will be normalized. Cannot be used with walletAddress."},{"schema":{"type":"string"},"in":"query","name":"startDate","required":false,"description":"Start date (YYYY-MM-DD)"},{"schema":{"type":"string"},"in":"query","name":"endDate","required":false,"description":"End date (YYYY-MM-DD)"},{"schema":{"type":"string","enum":["IN","OUT"]},"in":"query","name":"direction","required":false,"description":"Filter by direction"},{"schema":{"type":"string","enum":["ERC20","ERC721","ERC1155","NATIVE"]},"in":"query","name":"tokenType","required":false,"description":"Filter by token type"},{"schema":{"type":"string"},"in":"query","name":"tokenAddress","required":false,"description":"Filter by token address (0x… or comma-separated)"},{"schema":{"type":"string"},"in":"query","name":"hash","required":false,"description":"Filter by transaction hash"},{"schema":{"type":"string","enum":["mixed","crypto","fiat"],"default":"mixed"},"in":"query","name":"scope","required":false,"description":"Return both classes (`mixed`) or only one (`crypto`/`fiat`)."},{"schema":{"type":"string"},"in":"query","name":"blockchainId","required":false,"description":"Optional explicit chain selector for aggregated mode. Same effect as X-Blockchain-Id."},{"schema":{"type":"string","default":"1"},"in":"query","name":"page","required":false,"description":"Page number"},{"schema":{"type":"string","default":"50"},"in":"query","name":"limit","required":false,"description":"Items per page"},{"schema":{"type":"string","enum":["true","false"],"default":"true"},"in":"query","name":"includePrices","required":false,"description":"Attach real-time prices"}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1.2/users/me/lending":{"get":{"summary":"Lending catalog (full or user-scoped)","tags":["Privacy"],"description":"Returns active lending entries. Add `?userScoped=true` to restrict to chains where the authenticated user has watched addresses; omit it to get the full catalog. Use `X-Blockchain-Id` to filter by chain.","parameters":[{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"userScoped","required":false,"description":"If true, restrict to user watched chains"},{"schema":{"type":"string"},"in":"query","name":"blockchainId","required":false,"description":"Filter by chain ID"}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}}},"/v1.2/users/me/tokens":{"get":{"summary":"List user's tokens","tags":["Privacy"],"description":"Returns tokens the authenticated user has interacted with (based on transaction history). Use the X-Blockchain-Id header to filter by a single chain (flat array); omit it to get tokens grouped by blockchainId.","security":[{"JWT":[]}],"responses":{"200":{"description":"Flat array (with chain filter) or array of { blockchainId, tokens } objects (without chain filter).","content":{"application/json":{"schema":{"description":"Flat array (with chain filter) or array of { blockchainId, tokens } objects (without chain filter).","type":"array","items":{"type":"object","additionalProperties":true,"properties":{"address":{"type":"string"},"secondaryAddress":{"type":"string","nullable":true},"symbol":{"type":"string"},"name":{"type":"string"},"decimals":{"type":"number"},"blockchainId":{"type":"string"},"active":{"type":"boolean"},"iconUrl":{"type":"string","nullable":true},"type":{"type":"string","nullable":true}}}}}}}}}},"/v1.2/users/me/addressbook":{"get":{"summary":"List the authenticated user's unified address book","tags":["Users"],"description":"Returns entries stored in IBEXSAFE under `addressbook.entry.<uuid>`. Each entry has `name`, optional `label`, `userValidated`, `crypto` (chainId + address pairs), and `ibans` (populated by internal VOP verification flow when upstream returns MTCH).","security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}},"post":{"summary":"Create an address book entry","tags":["Users"],"description":"Creates a contact entry. Optional `crypto` seeds on-chain recipients. If `iban` + `respondingPspBic` are provided, the API runs VOP verification internally and attaches the IBAN row only on MTCH.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"label":{"type":"string"},"userValidated":{"type":"boolean","default":true},"iban":{"type":"string"},"respondingPspBic":{"type":"string"},"remittanceInfo":{"type":"string"},"crypto":{"type":"array","items":{"type":"object","required":["chainId","address"],"properties":{"chainId":{"type":"number"},"address":{"type":"string"}}}}}}}},"required":true},"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/users/me/addressbook/{id}":{"put":{"summary":"Update address book entry metadata","tags":["Users"],"description":"Partial update of `name`, `label`, `userValidated` only. Use sub-routes for crypto / IBAN rows.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"userValidated":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"},"in":"path","name":"id","required":true,"description":"Entry UUID"}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}},"delete":{"summary":"Remove an address book entry","tags":["Users"],"description":"Tombs `addressbook.entry.<id>` in IBEXSAFE (null value).","parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"},"in":"path","name":"id","required":true,"description":"Entry UUID"}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/users/me/addressbook/{id}/crypto":{"post":{"summary":"Add a crypto recipient to an entry","tags":["Users"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["chainId","address"],"properties":{"chainId":{"type":"number"},"address":{"type":"string"}}}}},"required":true},"parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"},"in":"path","name":"id","required":true,"description":"Entry UUID"}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/users/me/addressbook/{id}/crypto/{chainId}/{address}":{"delete":{"summary":"Remove a crypto recipient from an entry","tags":["Users"],"description":"chainId is numeric; address is the on-chain address (URL-encoded if needed).","parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"},"in":"path","name":"id","required":true,"description":"Entry UUID"},{"schema":{"type":"string"},"in":"path","name":"chainId","required":true,"description":"Numeric chain id"},{"schema":{"type":"string"},"in":"path","name":"address","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/users/me/addressbook/{id}/ibans/{iban}":{"delete":{"summary":"Remove an IBAN row from an entry","tags":["Users"],"description":"Does not call VOP; IBAN param is normalized (spaces stripped, upper case).","parameters":[{"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"},"in":"path","name":"id","required":true,"description":"Entry UUID"},{"schema":{"type":"string"},"in":"path","name":"iban","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1.2/ibexsafe/{verb}":{"post":{"summary":"IBEx Safe privacy manager","tags":["Privacy"],"description":"POST /v1.2/ibexsafe/ provides write/update flows for IBEX Safe key/value and email validation. It allows the consuming dApp to manage users' private data without ever storing it itself, by entrusting storage to IBEX Safe, a regulated entity (VASP) compliant with GDPR.\n\nFor a complete list of available verbs and detailed documentation, see the [IBEx Safe Endpoints](/IBEx_Safe) page.\n\n- POST `/v1.2/ibexsafe/userdata`\n  - Write body: `{ externalUserId, data: { 'key': value, ... } }`\n  - Read fallback body: `{ externalUserId, data: {} }`\n  - Expected response: upstream userData response (opaque JSON), relayed as-is (then flattened when needed).\n\n**Request example (JSON representation):**\n```json\n{\n  \"path\": \"/v1.2/ibexsafe/userdata\",\n  \"headers\": { \"Authorization\": \"Bearer eyJhbGciOi...\" },\n  \"body\": {\n    \"externalUserId\": \"<externalUserId>\",\n    \"data\": {\n      \"email\": \"jane.doe@foo.domain\",\n      \"firstName\": \"Jane\",\n      \"lastName\": \"Doe\",\n      \"language\": \"en\",\n      \"optin.newsletter\": true,\n      \"optin.walletAlerts\": true,\n      \"private.apiKey\": \"*****\"\n    }\n  }\n}\n```\n\n**Response example (200):**\n```json\n{\n  \"success\": true\n}\n```\n\n- POST `/v1.2/ibexsafe/validateEmail`\n  - Body: `{ email, externalUserId }`\n  - Expected response: validation code dispatch confirmation (shape defined by upstream)\n\n- POST `/v1.2/ibexsafe/confirmEmail`\n  - Body: `{ email, code, externalUserId, userDataName? ('marketing.email' by default), optinNews?, optinNotifications? }`","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"verb","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"get":{"summary":"IBEx Safe privacy manager","tags":["Privacy"],"description":"GET /v1.2/ibexsafe/ provides access to IBEX Safe key/value resources.\n\nFor a complete list of available verbs and detailed documentation, see the [IBEx Safe Endpoints](/IBEx_Safe) page.\n\nExample: `/v1.2/ibexsafe/userdata/external/{externalUserId}` returns all securely stored user data for the given externalUserId as a flat key/value map (or 204 when empty).\n\n**Request example (JSON representation):**\n```json\n{\n  \"path\": \"/v1.2/ibexsafe/userdata/external/{externalUserId}\",\n  \"headers\": { \"Authorization\": \"Bearer eyJhbGciOi...\" }\n}\n```\n\n**Response example (200):**\n```json\n{\n  \"email\": \"jane.doe@foo.domain\",\n  \"firstName\": \"Jane\",\n  \"lastName\": \"Doe\",\n  \"language\": \"en\",\n  \"optin.newsletter\": true,\n  \"optin.walletAlerts\": true\n}\n```\n\nPrivacy note: any key written via `POST /v1.2/ibexsafe/userdata/external/{externalUserId}` with the `private.` prefix is not returned by subsequent GET calls.","parameters":[{"schema":{"type":"string"},"in":"path","name":"verb","required":true}],"security":[{"JWT":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1.2/bcreader/{verb}":{"post":{"summary":"IBEx RPC data flow","tags":["Blockchain"],"description":"POST /v1.2/bcreader/{verb} provides data flows for IBEX RPC. It allows the consuming dApp to detect wallet's onchain activity on a trusted perimeter for both the total balance (/balances) and transactions (/transactions).\n\nFor a complete list of available endpoints and detailed documentation, see the [BCReader Endpoints](/BCReader) page.\n\n**Request example (balances):**\n```json\n{\n  \"path\": \"/v1.2/bcreader/balances\",\n  \"headers\": { \"Authorization\": \"Bearer eyJhbGciOi...\" },\n  \"body\": { \"address\": \"{safeAddress}\" }\n}\n```\n\n**Request example (transactions):**\n```json\n{\n  \"path\": \"/v1.2/bcreader/transactions\",\n  \"headers\": { \"Authorization\": \"Bearer eyJhbGciOi...\" },\n  \"body\": {\n    \"address\": \"{safeAddress}\",\n    \"startDate\": \"YYYY-MM-DD\",\n    \"endDate\": \"YYYY-MM-DD\",\n    \"limit\": 50,\n    \"page\": 1\n  }\n}\n```\n\nNotes: If `address` is omitted, the backend derives the first Safe address of the authenticated user for the current rpId.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"security":[{"JWT":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"verb","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"examples":{"example1":{"value":{"timestamp":"2025-06-02T13:30:45Z","balances":{"0x1234567890123456789012345678901234567890":{"address":"0x1234567890123456789012345678901234567890","balance":"123.456"},"0xabcdefabcdefabcdefabcdefabcdefabcdefabcd":{"address":"0xabcdefabcdefabcdefabcdefabcdefabcdefabcd","balance":"0"}}}},"example2":{"value":{"total":125,"page":1,"limit":20,"totalPages":7,"data":[{"transactionHash":"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef","from":"0x1234567890123456789012345678901234567890","to":"0xabcdefabcdefabcdefabcdefabcdefabcdefabcd","watchedAddress":"0x1234567890123456789012345678901234567890","direction":"IN","tokenAddress":"0xEURE12345678901234567890123456789012345678","tokenType":"ERC20","tokenSymbol":"EURE","tokenName":"EURe","tokenDecimals":18,"valueFormatted":"10.5","timestamp":"2025-06-02T13:30:00.000Z"}]}},"example3":{"value":[{"id":1,"blockchainId":"421614","provider":"AAVE","poolAddress":"0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951","addressesProvider":"0x012bAC54348C0E635dCAc9D5FB99f06F24136C9A","metadata":{"network":"arbitrum-sepolia","version":"v3"},"active":true,"supplyToken":{"address":"0xWBTC123456789012345678901234567890123456","symbol":"WBTC","name":"Wrapped BTC","decimals":8},"borrowToken":{"address":"0xWBTC123456789012345678901234567890123456","symbol":"WBTC","name":"Wrapped BTC","decimals":8},"poolToken":{"address":"0xaEthUSDC12345678901234567890123456789012","symbol":"aEthUSDC","name":"Aave Arbitrum Sepolia USDC aToken","decimals":6}}]}}}}}}},"get":{"summary":"IBEx RPC data flow","tags":["Blockchain"],"description":"GET /v1.2/bcreader/{verb} provides data flows for IBEX RPC. It allows the consuming dApp to detect wallet's onchain activity on a trusted perimeter for both the total balance (/balances) and transactions (/transactions).\n\nFor a complete list of available endpoints and detailed documentation, see the [BCReader Endpoints](/BCReader) page.\n\n**Parameters:**\n\n- `address` (string, optional): EVM address (0x format) of the Safe wallet. If omitted, the backend derives the first Safe address of the authenticated user for the current rpId.\n- `blockchainId` (number, optional): Target blockchain network ID. Can be provided via:\n  - Header: `X-Blockchain-Id: <id>` (preferred)\n  - Query parameter: `?blockchainId=<id>`\n  - If omitted, a default from configuration is applied.\n- `startDate` (string, optional): Start date for filtering transactions (ISO format: `YYYY-MM-DD`).\n- `endDate` (string, optional): End date for filtering transactions (ISO format: `YYYY-MM-DD`). Date filters are inclusive.\n- `limit` (number, optional): Maximum number of results to return.\n- `page` (number, optional): Page number for pagination (alternative to `offset`).\n- `offset` (number, optional): Number of results to skip for pagination (alternative to `page`).\n\n**Example:**\n- GET `/v1.2/bcreader/balances?address={safeAddress}`\n  - Method: GET\n  - Headers: `Authorization`\n  - Expected response: upstream BCReader balances payload (opaque JSON), relayed as-is.\n  - Notes:\n    - If `address` is omitted, the backend derives the first Safe address of the authenticated user for the current rpId.\n- GET `/v1.2/bcreader/transactions?address={safeAddress}&startDate=YYYY-MM-DD&endDate=YYYY-MM-DD&limit={n}&(offset|page)={n}`\n  - Method: GET\n  - Headers: `Authorization`\n  - Notes: date filters are inclusive; pagination supported via `offset` or `page`.","security":[{"JWT":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"verb","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"examples":{"example1":{"value":{"timestamp":"2025-06-02T13:30:45Z","balances":{"0x1234567890123456789012345678901234567890":{"address":"0x1234567890123456789012345678901234567890","balance":"123.456"},"0xabcdefabcdefabcdefabcdefabcdefabcdefabcd":{"address":"0xabcdefabcdefabcdefabcdefabcdefabcdefabcd","balance":"0"}}}},"example2":{"value":{"total":125,"page":1,"limit":20,"totalPages":7,"data":[{"transactionHash":"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef","from":"0x1234567890123456789012345678901234567890","to":"0xabcdefabcdefabcdefabcdefabcdefabcdefabcd","watchedAddress":"0x1234567890123456789012345678901234567890","direction":"IN","tokenAddress":"0xEURE12345678901234567890123456789012345678","tokenType":"ERC20","tokenSymbol":"EURE","tokenName":"EURe","tokenDecimals":18,"valueFormatted":"10.5","timestamp":"2025-06-02T13:30:00.000Z"}]}},"example3":{"value":[{"id":1,"blockchainId":"421614","provider":"AAVE","poolAddress":"0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951","addressesProvider":"0x012bAC54348C0E635dCAc9D5FB99f06F24136C9A","metadata":{"network":"arbitrum-sepolia","version":"v3"},"active":true,"supplyToken":{"address":"0xWBTC123456789012345678901234567890123456","symbol":"WBTC","name":"Wrapped BTC","decimals":8},"borrowToken":{"address":"0xWBTC123456789012345678901234567890123456","symbol":"WBTC","name":"Wrapped BTC","decimals":8},"poolToken":{"address":"0xaEthUSDC12345678901234567890123456789012","symbol":"aEthUSDC","name":"Aave Arbitrum Sepolia USDC aToken","decimals":6}}]}}}}}}}},"/v1.2/domain/users/{id}":{"get":{"summary":"Tenant user by externalUserId","tags":["Domains","Privacy"],"description":"Returns user information for the given externalUserId within the current tenant rpId resolved from host/origin and x-api-key. Includes KY status, signers, and safes.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"externalUserId in current tenant"}],"security":[{"API_KEY":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","example":"c76302cb-f845-40f4-9c56-29710323afba"},"ky":{"type":"string","example":"2"},"userdata":{"type":"object","additionalProperties":true,"description":"Flat key/value user data stored in IBEX Safe. Keys prefixed with `private.` are write-only and never returned. Internal `audit.*` keys (managed by the wrapper for IP/User-Agent rotation) are also hidden."},"signers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"AVZs0qRCBSmfThZWu37gK4nJR2w"},"safes":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string","example":"0xd676c6188195372EC269E9C2cAf815C56436A679"},"threshold":{"type":"number","example":1},"iban":{"type":"object","properties":{"chainId":{"type":"number","example":100},"iban":{"type":"string","example":"FR7630006000011234567890189"},"bic":{"type":"string","example":"BNPAFRPPXXX"}}}}}}}}}}}}}}}}},"/v1.2/domain/chainid":{"get":{"summary":"Tenant chains and wallets","tags":["Domains","Blockchain"],"description":"Returns active chains enriched with wallets and module status for the whole current tenant (rpId). Response shape mirrors GET /v1.2/users/me/chainid.","security":[{"API_KEY":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"defaultChainId":{"type":"number"},"chains":{"type":"array","items":{"type":"object","properties":{"chainId":{"type":"number"},"chainName":{"type":"string"},"modules":{"type":"object","properties":{"recovery":{"oneOf":[{"type":"boolean"},{"type":"string","enum":["N/A"]}]},"automation":{"oneOf":[{"type":"boolean"},{"type":"string","enum":["N/A"]}]},"multisig":{"oneOf":[{"type":"boolean","enum":[true]},{"type":"string","enum":["N/A"]}]}}},"wallets":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"}}}}}}}}}}}}}}},"/v1.2/domain/kv":{"get":{"summary":"Get domain key/value store","tags":["Domains"],"description":"Returns the JSON object stored for the **current domain** (resolved from the request host / rpId). Requires `x-api-key` matching that domain. If nothing was saved yet, returns `{ \"data\": {} }`. Recommended webhook contract: `{ \"webhooks\": { \"userEvents\": { \"enabled\": true, \"url\": \"https://tenant.example/webhook\", \"events\": [\"user.ky.updated\",\"user.iban.updated\"], \"headers\": {\"X-Webhook-Key\":\"...\"}, \"timeoutMs\": 3000 } } }`.","security":[{"API_KEY":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","additionalProperties":true}},"required":["data"]}}}}}},"put":{"summary":"Replace domain key/value store","tags":["Domains"],"description":"Replaces the entire stored JSON object for the current domain. Body: `{ \"data\": { ... } }`. Maximum serialized size: 1048576 bytes. Suggested key for tenant webhooks: `webhooks.userEvents` (`enabled`, `url`, optional `events`, optional `headers`, optional `timeoutMs`).","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","additionalProperties":true}}}}},"required":true},"security":[{"API_KEY":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","additionalProperties":true}},"required":["data"]}}}}}},"patch":{"summary":"Merge keys into domain key/value store","tags":["Domains"],"description":"Shallow-merge top-level keys from `patch` into the existing stored object. Body: `{ \"patch\": { \"key\": value, ... } }`. Maximum serialized size after merge: 1048576 bytes. Useful for progressive rollout of webhook config under `webhooks.userEvents`.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["patch"],"properties":{"patch":{"type":"object","additionalProperties":true}}}}},"required":true},"security":[{"API_KEY":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","additionalProperties":true}},"required":["data"]}}}}}}},"/api/admin/billing/pricing":{"get":{"summary":"List pricing rules","tags":["Admin"],"description":"Returns all per-domain per-operation pricing rules. Use `rpId` query parameter to filter by domain.","parameters":[{"schema":{"type":"string"},"in":"query","name":"rpId","required":false,"description":"Filter by domain rpId"}],"security":[{"API_KEY":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"number"},"operationType":{"type":"string","enum":["TRANSFER_EURe","TRANSFER_TOKEN","MONERIUM_CREATE_IBAN","MONERIUM_WITHDRAW_EURe","SIGN_MESSAGE","ENABLE_RECOVERY","INITIATE_RECOVERY","EXECUTE_RECOVERY","CANCEL_RECOVERY","AAVE_SUPPLY","AAVE_WITHDRAW","SWAP_FROM_QUOTE","BITCOIN_SEND","HYPERLIQUID_ENTER_VAULT","HYPERLIQUID_WITHDRAW_VAULT","HYPERLIQUID_WITHDRAW","HYPERLIQUID_DEPOSIT","ADD_OWNER","REMOVE_OWNER","CHANGE_THRESHOLD","ENABLE_AUTOMATION_MODULE"]},"priceEur":{"type":"number","description":"Price in EUR for this operation"},"domainRpId":{"type":"string","description":"Domain rpId"}}}}}}}}},"put":{"summary":"Upsert pricing rule","tags":["Admin"],"description":"Create or update a pricing rule for a given domain and operation type. The combination `(rpId, operationType)` is unique.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["rpId","operationType","priceEur"],"properties":{"rpId":{"type":"string","description":"Domain rpId"},"operationType":{"type":"string","enum":["TRANSFER_EURe","TRANSFER_TOKEN","MONERIUM_CREATE_IBAN","MONERIUM_WITHDRAW_EURe","SIGN_MESSAGE","ENABLE_RECOVERY","INITIATE_RECOVERY","EXECUTE_RECOVERY","CANCEL_RECOVERY","AAVE_SUPPLY","AAVE_WITHDRAW","SWAP_FROM_QUOTE","BITCOIN_SEND","HYPERLIQUID_ENTER_VAULT","HYPERLIQUID_WITHDRAW_VAULT","HYPERLIQUID_WITHDRAW","HYPERLIQUID_DEPOSIT","ADD_OWNER","REMOVE_OWNER","CHANGE_THRESHOLD","ENABLE_AUTOMATION_MODULE"],"description":"Operation type"},"priceEur":{"type":"number","description":"Price in EUR (e.g. 0.05)"}}}}},"required":true},"security":[{"API_KEY":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"number"},"operationType":{"type":"string"},"priceEur":{"type":"number"},"domainRpId":{"type":"string"}}}}}}}},"delete":{"summary":"Delete pricing rule","tags":["Admin"],"description":"Delete a pricing rule for a given domain and operation type.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["rpId","operationType"],"properties":{"rpId":{"type":"string","description":"Domain rpId"},"operationType":{"type":"string","enum":["TRANSFER_EURe","TRANSFER_TOKEN","MONERIUM_CREATE_IBAN","MONERIUM_WITHDRAW_EURe","SIGN_MESSAGE","ENABLE_RECOVERY","INITIATE_RECOVERY","EXECUTE_RECOVERY","CANCEL_RECOVERY","AAVE_SUPPLY","AAVE_WITHDRAW","SWAP_FROM_QUOTE","BITCOIN_SEND","HYPERLIQUID_ENTER_VAULT","HYPERLIQUID_WITHDRAW_VAULT","HYPERLIQUID_WITHDRAW","HYPERLIQUID_DEPOSIT","ADD_OWNER","REMOVE_OWNER","CHANGE_THRESHOLD","ENABLE_AUTOMATION_MODULE"],"description":"Operation type"}}}}},"required":true},"security":[{"API_KEY":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"deleted":{"type":"object","properties":{"operationType":{"type":"string"},"domainRpId":{"type":"string"}}}}}}}}}}},"/api/admin/billing/transactions":{"get":{"summary":"List billing transactions","tags":["Admin"],"description":"Returns billing transaction history. Supports filtering by `rpId`, `status`, pagination via `limit`/`offset`.","parameters":[{"schema":{"type":"string"},"in":"query","name":"rpId","required":false,"description":"Filter by domain rpId"},{"schema":{"type":"string","enum":["PENDING","SENT","CONFIRMED","FAILED"]},"in":"query","name":"status","required":false,"description":"Filter by status"},{"schema":{"type":"number","default":50},"in":"query","name":"limit","required":false,"description":"Max results (default 50, max 200)"},{"schema":{"type":"number","default":0},"in":"query","name":"offset","required":false,"description":"Offset for pagination"}],"security":[{"API_KEY":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"rpId":{"type":"string"},"type":{"type":"string"},"userOpHash":{"type":"string"},"blockchainId":{"type":"number"},"ibxAmount":{"type":"number"},"eurPerIbx":{"type":"number"},"txHash":{"type":"string","nullable":true},"status":{"type":"string"},"error":{"type":"string","nullable":true}}}}}}}}}}}}},"tags":[{"name":"Authentication","description":"Authentication endpoints"},{"name":"Blockchain","description":"Blockchain and Safe operations"},{"name":"Privacy","description":"Privacy and data management"},{"name":"Domains","description":"Domain management"},{"name":"Infra","description":"Infrastructure endpoints"},{"name":"WIP","description":"Work in progress / experimental"},{"name":"Admin","description":"Admin endpoints"}]}