{
  "openapi": "3.1.0",
  "info": {
    "title": "Sigil API",
    "version": "3.0.0",
    "summary": "Public HTTP API for the Sigil self-custodial wallet platform.",
    "description": "Three lanes by audience and auth model:\n\n- **Wallet** (`/v1/wallets/...`): publishable key + end-user JWT. Used\n  by the Sigil iframe through `@sigilkeys/sdk`. You can also call these\n  directly if you build your own client.\n- **Server-to-server** (`/v1/s2s/...`): secret key (`sk_live_…`).\n  For your backend. Keys support IP allowlisting and scopes (`full` or `read`).\n- **TEE signing** (`/v1/wallets/me/sign`, `/v1/s2s/sign`): available only\n  for organizations using TEE signing mode. Signing happens server-side\n  inside a hardware-attested Trusted Execution Environment.\n\nProduction base URL: `https://api.sigilkeys.com`.\n\nEvery JSON response uses one envelope: `{ \"data\": ..., \"error\": null }`\nor `{ \"data\": null, \"error\": { \"code\": \"...\", \"message\": \"...\" } }`.\nError messages are always user-safe and never leak internal details.\n\n**Security details:** see [Security model](https://developers.sigilkeys.com/wallets/security/)\nand [TEE security model](https://developers.sigilkeys.com/wallets/tee/).\n",
    "contact": {
      "name": "Sigil developers",
      "url": "https://developers.sigilkeys.com"
    }
  },
  "servers": [
    {
      "url": "https://api.sigilkeys.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Health"
    },
    {
      "name": "Wallet auth",
      "description": "Email OTP for the Sigil-hosted end-user auth mode."
    },
    {
      "name": "Wallets",
      "description": "End-user wallet lifecycle (multi-network)."
    },
    {
      "name": "Recovery",
      "description": "Recover a wallet on a new device."
    },
    {
      "name": "EVM transactions",
      "description": "Raw EIP-1559 transactions from the user's EOA."
    },
    {
      "name": "TEE signing",
      "description": "Server-side signing for organizations in TEE mode. Available via the\nend-user lane (interactive) and the S2S lane (agent/backend).\n"
    },
    {
      "name": "S2S Wallets",
      "description": "Back-office wallet listing and management for your backend."
    },
    {
      "name": "Smart accounts",
      "description": "ERC-4337 LightAccount v2 records sitting on top of an EOA wallet.\nOpt-in per chain. Sigil sponsors gas via Pimlico, capped at\n€10 / wallet / calendar month (EUR-equivalent across chains).\n"
    },
    {
      "name": "UserOps",
      "description": "ERC-4337 build → sign → submit pipeline for the gas-sponsored\nlane. Only routes that have a smart-account row for the requested\nchain are accepted; otherwise the SDK falls back to the EOA\n`/v1/wallets/evm/*` path.\n"
    },
    {
      "name": "Agents (A2A)",
      "description": "Public Agent-to-Agent surface for inter-agent delegation. Implements a\npragmatic subset of the [A2A protocol](https://google.github.io/A2A/):\neach Sigil agent exposes a Discovery card and a JSON-RPC endpoint that\naccepts `tasks/send` / `tasks/get` from authenticated callers (other\nSigil agents, external A2A clients).\n"
    },
    {
      "name": "Agent webhooks",
      "description": "Inbound HTTP webhooks that trigger an agent task. The customer\nconfigures the URL + an HMAC secret in the portal; their upstream\nsystem POSTs payloads here.\n"
    }
  ],
  "security": [],
  "paths": {
    "/v1/health": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "Liveness check",
        "responses": {
          "200": {
            "description": "Always 200 if the API is up.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                },
                "example": {
                  "data": {
                    "status": "ok"
                  },
                  "error": null
                }
              }
            }
          }
        }
      }
    },
    "/v1/wallets/auth/email-otp/start": {
      "post": {
        "tags": [
          "Wallet auth"
        ],
        "summary": "Start the Sigil-hosted email-OTP login",
        "description": "Publishable key only — the user doesn't have a JWT yet. Returns\n204 regardless of whether the email is registered (no enumeration leak).\n\nRate limit: max 5 OTP codes per (org, email) in a rolling window.\n",
        "security": [
          {
            "PublishableKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "OTP queued for delivery."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/wallets/auth/email-otp/verify": {
      "post": {
        "tags": [
          "Wallet auth"
        ],
        "summary": "Verify an OTP and receive an end-user JWT",
        "description": "Max 5 verification attempts per OTP code. After 5 failures the\ncode is invalidated and the user must request a new one.\n\nThe returned JWT has a **1-hour TTL** with no refresh token.\nWhen the token expires, the user re-authenticates via OTP.\nTokens can be revoked server-side via `POST /v1/wallets/me/logout`.\n",
        "security": [
          {
            "PublishableKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "code"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "code": {
                    "type": "string",
                    "example": "449922"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JWT minted (1h TTL).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "token": {
                              "type": "string",
                              "description": "HS256 JWT, 1h TTL"
                            },
                            "user_identity_id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "email": {
                              "type": "string",
                              "format": "email"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/wallets/identify": {
      "post": {
        "tags": [
          "Wallets"
        ],
        "summary": "Auth canary — confirms the publishable key + JWT line up",
        "description": "Also returns the organization's `supported_networks` and `signing_mode`\nso the client knows which key management model applies.\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Identity"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/wallets/me": {
      "get": {
        "tags": [
          "Wallets"
        ],
        "summary": "Get the primary active wallet for the authenticated end user",
        "description": "Returns the most recently created active wallet (defaults to secp256k1).\nUse `GET /v1/wallets/me/all` to retrieve wallets for all curves.\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "responses": {
          "200": {
            "description": "Wallet record.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Wallet"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/wallets/me/all": {
      "get": {
        "tags": [
          "Wallets"
        ],
        "summary": "List all active wallets for the authenticated end user",
        "description": "Returns one wallet per curve (secp256k1, ed25519, etc.) depending\non which networks the organization has enabled.\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "responses": {
          "200": {
            "description": "Array of active wallets across all curves.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Wallet"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/wallets": {
      "post": {
        "tags": [
          "Wallets"
        ],
        "summary": "Create a wallet",
        "description": "Creates one wallet per curve. Idempotent on\n`(organization_id, user_identity_id, curve)`. A duplicate\nreturns `409 wallet_exists` with the existing wallet.\n\n**Client mode:** the request body must include key shares generated\nin the browser iframe. The `provider_share`, `recovery_share`, and\ntheir indices are required.\n\n**TEE mode:** key generation happens server-side inside the TEE.\nThe share fields should be omitted — the server generates the key,\nsplits it, and encrypts the shares automatically. Only `curve` and\noptionally `account_type` are needed.\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "address": {
                    "type": "string",
                    "description": "Required for client mode. Omit for TEE mode (derived server-side)."
                  },
                  "public_key": {
                    "type": "string",
                    "description": "0x-prefixed hex. Required for client mode. Omit for TEE mode."
                  },
                  "chain_type": {
                    "type": "string"
                  },
                  "curve": {
                    "type": "string",
                    "enum": [
                      "secp256k1",
                      "ed25519"
                    ],
                    "default": "secp256k1",
                    "description": "Cryptographic curve. secp256k1 for EVM/Bitcoin/Tron/Cosmos,\ned25519 for Solana/Near/Aptos/Stellar. Defaults to secp256k1.\n"
                  },
                  "provider_share": {
                    "type": "string",
                    "description": "base64. Required for client mode. Omit for TEE mode."
                  },
                  "provider_share_index": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Required for client mode. Omit for TEE mode."
                  },
                  "recovery_share": {
                    "type": "string",
                    "description": "base64. Required for client mode. Omit for TEE mode."
                  },
                  "recovery_share_index": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Required for client mode. Omit for TEE mode."
                  },
                  "account_type": {
                    "type": "string",
                    "enum": [
                      "eoa"
                    ],
                    "default": "eoa"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Wallet created.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Wallet"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/wallets/me/shares/provider": {
      "post": {
        "tags": [
          "Wallets"
        ],
        "summary": "Return the provider share so the iframe can sign",
        "description": "**Client mode only.** Returns the decrypted provider share for the\nspecified curve so the iframe can reconstruct the key and sign locally.\n\nReturns `400 signing_mode_mismatch` if the organization uses TEE mode.\n\nRate limited: max 3 burst, ~1 request per 5 seconds per user.\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "parameters": [
          {
            "name": "curve",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "secp256k1",
                "ed25519"
              ],
              "default": "secp256k1"
            },
            "description": "Which curve's provider share to return."
          }
        ],
        "responses": {
          "200": {
            "description": "Cleartext provider share.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "wallet_id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "provider_share": {
                              "type": "string",
                              "description": "base64"
                            },
                            "share_index": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Not available in TEE mode.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/wallets/me/logout": {
      "post": {
        "tags": [
          "Wallets"
        ],
        "summary": "Revoke the current JWT",
        "description": "Adds the current token to the server-side revocation list.\nThe token is immediately invalid for all subsequent requests,\neven before its natural 1h expiry.\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "responses": {
          "204": {
            "description": "Token revoked."
          }
        }
      }
    },
    "/v1/wallets/me/sign": {
      "post": {
        "tags": [
          "TEE signing"
        ],
        "summary": "Sign a message or transaction inside the TEE",
        "description": "**TEE mode only.** The authenticated end user requests a signature\nfor the specified payload. The TEE reconstructs the private key\nfrom its encrypted shares, signs, and wipes all key material.\n\nReturns `400 signing_mode_mismatch` if the organization uses client mode.\n\nRate limited: max 3 burst, ~1 request per 5 seconds per user.\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signature produced inside the TEE.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/SignResponse"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Not available in client mode or invalid payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/wallets/me/recovery/initiate": {
      "post": {
        "tags": [
          "Recovery"
        ],
        "summary": "Email an OTP to start a recovery process",
        "description": "**Client mode only.** Starts a recovery process with a 15-minute TTL.\nThe OTP is sent to the user's registered email address.\n\nTEE-mode organizations do not use recovery (no device share exists).\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "responses": {
          "200": {
            "description": "Recovery process id (pass to verify).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "recovery_process_id": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/wallets/me/recovery/{id}/verify": {
      "post": {
        "tags": [
          "Recovery"
        ],
        "summary": "Verify the OTP and return both shares",
        "description": "Max 5 verification attempts per recovery process. After 5 failures,\nthe process is locked and the user must start a new one.\nThe recovery process expires after 15 minutes.\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Both shares returned (iframe rebuilds the key in memory).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "wallet_id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "provider_share": {
                              "type": "string",
                              "description": "base64"
                            },
                            "recovery_share": {
                              "type": "string",
                              "description": "base64"
                            },
                            "polynomial_version": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "410": {
            "description": "Recovery process expired."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/wallets/me/recovery/{id}/complete": {
      "post": {
        "tags": [
          "Recovery"
        ],
        "summary": "Persist new shares with a fresh polynomial",
        "description": "Rotates all key material to a new polynomial version, invalidating\nold shares. A notification email is sent to the user confirming\nthe recovery completion with timestamp.\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "new_provider_share",
                  "new_provider_share_index",
                  "new_recovery_share",
                  "new_recovery_share_index"
                ],
                "properties": {
                  "new_provider_share": {
                    "type": "string",
                    "description": "base64"
                  },
                  "new_provider_share_index": {
                    "type": "integer"
                  },
                  "new_recovery_share": {
                    "type": "string",
                    "description": "base64"
                  },
                  "new_recovery_share_index": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Recovery completed. Notification email sent."
          }
        }
      }
    },
    "/v1/wallets/evm/prepare-tx": {
      "post": {
        "tags": [
          "EVM transactions"
        ],
        "summary": "Prepare an unsigned EIP-1559 transaction",
        "description": "Resolves nonce, gas parameters, and chainId for the user's EOA\non the specified chain. The iframe signs the prepared transaction\nlocally and broadcasts via `POST /v1/wallets/evm/broadcast-tx`.\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "chain",
                  "to"
                ],
                "properties": {
                  "chain": {
                    "type": "string",
                    "description": "e.g. ethereum, base, arbitrum"
                  },
                  "to": {
                    "type": "string",
                    "description": "0x-prefixed contract address"
                  },
                  "data": {
                    "type": "string",
                    "description": "0x-prefixed calldata"
                  },
                  "value": {
                    "type": "string",
                    "description": "decimal wei"
                  },
                  "gas_limit": {
                    "type": "string",
                    "description": "decimal gas units (override)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Unsigned transaction parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "chain": {
                              "type": "string"
                            },
                            "chain_id": {
                              "type": "string",
                              "description": "0x hex"
                            },
                            "nonce": {
                              "type": "string",
                              "description": "0x hex"
                            },
                            "from": {
                              "type": "string"
                            },
                            "to": {
                              "type": "string"
                            },
                            "data": {
                              "type": "string"
                            },
                            "value": {
                              "type": "string",
                              "description": "0x hex"
                            },
                            "gas_limit": {
                              "type": "string",
                              "description": "0x hex"
                            },
                            "max_fee_per_gas": {
                              "type": "string",
                              "description": "0x hex"
                            },
                            "max_priority_fee_per_gas": {
                              "type": "string",
                              "description": "0x hex"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/wallets/evm/broadcast-tx": {
      "post": {
        "tags": [
          "EVM transactions"
        ],
        "summary": "Broadcast a signed EIP-1559 transaction",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "chain",
                  "raw_tx"
                ],
                "properties": {
                  "chain": {
                    "type": "string"
                  },
                  "raw_tx": {
                    "type": "string",
                    "description": "0x-prefixed RLP-encoded signed tx"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transaction submitted.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "chain": {
                              "type": "string"
                            },
                            "chain_id": {
                              "type": "string"
                            },
                            "tx_hash": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/wallets/me/smart-accounts": {
      "get": {
        "tags": [
          "Smart accounts"
        ],
        "summary": "List smart accounts for the authenticated end user",
        "description": "Returns every ERC-4337 smart account provisioned on top of the\nuser's secp256k1 (EVM) wallet, across chains. The address is the\nsame across EVM chains for the same (wallet, factory) — but each\nrow tracks per-chain deployed state and sponsor policy.\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of smart accounts (one per (wallet, chain, factory)).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "items": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/SmartAccount"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Smart accounts"
        ],
        "summary": "Provision a smart account on a given EVM chain",
        "description": "Opt-in step the user (or your iframe) triggers per chain. The\nendpoint derives the counterfactual LightAccount v2 address by\neth_call against the factory's `getAddress(owner, salt=0)` view\nfunction and records one (wallet, chain, factory) row.\n\nIdempotent — calling it twice for the same (wallet, chain) returns\nthe existing row.\n\nRefuses non-secp256k1 wallets with `invalid_curve` (smart accounts\nare EVM-only).\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "chain"
                ],
                "properties": {
                  "chain": {
                    "type": "string",
                    "description": "EVM chain handle, e.g. base, arbitrum, ethereum"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Smart account provisioned.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/SmartAccount"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Wallet's curve is not secp256k1, or chain handle unknown.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/v1/userop/build": {
      "post": {
        "tags": [
          "UserOps"
        ],
        "summary": "Build and sponsor a UserOp",
        "description": "Resolves the wallet's smart account for the target chain, encodes\nthe calls into LightAccount v2's `execute` (single call) or\n`executeBatch` (multiple) calldata, asks the bundler (Pimlico) to\nsponsor and estimate gas, then persists the half-built op so the\nsigning client can later splice its signature and submit.\n\nReturns `409 smart_account_not_provisioned` if the wallet has no\nSA row for the requested chain — call `POST\n/v1/wallets/me/smart-accounts` first.\n\nReturns `402 gas_cap_exceeded` if the wallet has burned its\nmonthly sponsorship budget (€10 / wallet / calendar month).\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "chain",
                  "calls"
                ],
                "properties": {
                  "chain": {
                    "type": "string",
                    "description": "EVM chain handle"
                  },
                  "calls": {
                    "type": "array",
                    "description": "One or more inner calls the smart account will execute.",
                    "items": {
                      "type": "object",
                      "required": [
                        "to"
                      ],
                      "properties": {
                        "to": {
                          "type": "string"
                        },
                        "data": {
                          "type": "string",
                          "description": "0x-prefixed calldata; default 0x"
                        },
                        "value": {
                          "type": "string",
                          "description": "decimal wei; default 0"
                        }
                      }
                    }
                  },
                  "preview": {
                    "$ref": "#/components/schemas/SignPreview"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "UserOp ready to sign.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "user_op_hash": {
                              "type": "string",
                              "description": "0x-prefixed 32-byte hash to sign with the owner EOA"
                            },
                            "preview": {
                              "$ref": "#/components/schemas/SignPreview"
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "description": "Monthly sponsorship cap reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "No smart account provisioned for this wallet on the requested chain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/v1/userop/submit": {
      "post": {
        "tags": [
          "UserOps"
        ],
        "summary": "Splice the owner signature, submit to the bundler, await receipt",
        "description": "The signing client (iframe or backend) signs the `user_op_hash`\nreturned by `/v1/userop/build` with the owner EOA's key\n(EIP-191-wrapped). This endpoint splices that signature onto the\nstored UserOp, sends it via the bundler, then polls up to 30 s\nfor the on-chain receipt.\n\nReturns `status: \"submitted\"` if the bundler accepted but the op\ndidn't land in time — clients poll `GET /v1/userop/{hash}` until\n`confirmed` or `failed`.\n",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "user_op_hash",
                  "signature"
                ],
                "properties": {
                  "user_op_hash": {
                    "type": "string"
                  },
                  "signature": {
                    "type": "string",
                    "description": "0x-prefixed 65-byte r||s||v ECDSA signature over the EIP-191-wrapped hash"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Submitted (and possibly already confirmed).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/UserOpStatus"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/userop/{hash}": {
      "get": {
        "tags": [
          "UserOps"
        ],
        "summary": "Poll the status of a previously submitted UserOp",
        "security": [
          {
            "PublishableKey": [],
            "EndUserJWT": []
          }
        ],
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "user_op_hash returned by /v1/userop/build"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current status.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/UserOpStatus"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/s2s/wallets": {
      "get": {
        "tags": [
          "S2S Wallets"
        ],
        "summary": "List wallets in the org",
        "description": "Requires a secret key (`sk_live_…`). Keys with `scope: read` have\naccess. Keys with IP allowlisting are restricted to listed CIDRs.\nRate limit: 30 req/s sustained, 60 burst per key.\n",
        "security": [
          {
            "SecretKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Offset"
          }
        ],
        "responses": {
          "200": {
            "description": "Paged list.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "items": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/WalletWithIdentity"
                              }
                            },
                            "total": {
                              "type": "integer"
                            },
                            "limit": {
                              "type": "integer"
                            },
                            "offset": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/s2s/wallets/{walletID}": {
      "get": {
        "tags": [
          "S2S Wallets"
        ],
        "summary": "Read one wallet",
        "security": [
          {
            "SecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "walletID",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Wallet.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WalletWithIdentity"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/s2s/wallets/{walletID}/archive": {
      "post": {
        "tags": [
          "S2S Wallets"
        ],
        "summary": "Archive a compromised wallet",
        "description": "Moves the wallet to `archived` status. Archived wallets cannot\nsign — the iframe rejects operations on them (client mode) or the\nTEE refuses to reconstruct the key (TEE mode). This is the kill\nswitch for compromised wallets. Requires `scope: full`.\n",
        "security": [
          {
            "SecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "walletID",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Wallet archived."
          },
          "403": {
            "description": "API key has read-only scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/s2s/sign": {
      "post": {
        "tags": [
          "TEE signing"
        ],
        "summary": "Sign a payload server-to-server via the TEE",
        "description": "**TEE mode only.** Your backend sends a signing request using a\nsecret key. The TEE reconstructs the private key from encrypted\nshares, signs the payload, and wipes all key material.\n\nThis endpoint enables agent-driven and automated signing workflows\nwithout any browser interaction. Requires `scope: full`.\n\nReturns `400 signing_mode_mismatch` if the organization uses client mode.\n\nRate limit: 30 req/s sustained, 60 burst per key.\n",
        "security": [
          {
            "SecretKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SignRequest"
                  },
                  {
                    "type": "object",
                    "required": [
                      "wallet_id"
                    ],
                    "properties": {
                      "wallet_id": {
                        "type": "string",
                        "format": "uuid",
                        "description": "The wallet to sign with."
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signature produced inside the TEE.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/SignResponse"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Not available in client mode or invalid payload.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "API key has read-only scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/s2s/wallets/{walletID}/smart-accounts": {
      "get": {
        "tags": [
          "Smart accounts"
        ],
        "summary": "List smart accounts for a wallet",
        "description": "Owner-facing read of the same data the end-user lane returns.\nUseful for back-office dashboards.\n",
        "security": [
          {
            "SecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "walletID",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of smart accounts on top of this wallet.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "items": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/SmartAccount"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Smart accounts"
        ],
        "summary": "Provision a smart account on behalf of an end user",
        "description": "Symmetric to `POST /v1/wallets/me/smart-accounts` but driven by\nthe owner platform — useful when you want to seed smart accounts\nat onboarding without prompting the user. Requires `scope: full`.\n",
        "security": [
          {
            "SecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "walletID",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "chain"
                ],
                "properties": {
                  "chain": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Smart account provisioned (created_by = owner).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/SmartAccount"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key has read-only scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/agents/{agentID}/.well-known/agent-card.json": {
      "get": {
        "tags": [
          "Agents (A2A)"
        ],
        "summary": "Discovery card for an agent",
        "description": "Returns the agent's public Discovery card per the A2A protocol:\ncapabilities, supported tasks, JSON-RPC endpoint, contact info.\nAlways public — no auth required — so external A2A clients can\nintrospect before sending tasks.\n",
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "agentID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A2A Discovery card (JSON).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "A2A `AgentCard` document. Includes `id`, `name`, `description`, `capabilities`, and the JSON-RPC `endpoint_url`."
                }
              }
            }
          },
          "404": {
            "description": "Agent does not exist or is archived."
          }
        }
      }
    },
    "/v1/agents/{agentID}/a2a": {
      "post": {
        "tags": [
          "Agents (A2A)"
        ],
        "summary": "Send an Agent-to-Agent JSON-RPC request",
        "description": "JSON-RPC 2.0 endpoint. Supported methods:\n\n- `tasks/send` — submit a new task (objective + optional metadata).\n- `tasks/get` — poll a previously submitted task by id.\n\nAuthentication is a bearer token in the `Authorization` header. For\nSigil-to-Sigil delegation the token is an `ac_live_…` JWT minted by\nthe calling agent's runner; for external A2A clients you provide a\nSigil-issued API token bound to the target agent (`as_live_…`).\n",
        "security": [
          {
            "SigilJWT": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "agentID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method",
                  "id"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "enum": [
                      "2.0"
                    ]
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "tasks/send",
                      "tasks/get"
                    ]
                  },
                  "id": {
                    "description": "JSON-RPC request id (string or number)."
                  },
                  "params": {
                    "type": "object",
                    "description": "For `tasks/send`: `{ id, message: { role, parts: [{type: \"text\", text}] } }`.\nFor `tasks/get`:  `{ id }`.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC 2.0 response envelope. `result` carries an A2A `Task` with `status.state` ∈ submitted | working | input-required | completed | canceled | failed and an optional `artifacts[]` array.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "jsonrpc",
                    "id"
                  ],
                  "properties": {
                    "jsonrpc": {
                      "type": "string",
                      "enum": [
                        "2.0"
                      ]
                    },
                    "id": {
                      "description": "Mirror of the request id."
                    },
                    "result": {
                      "type": "object",
                      "description": "A2A Task object."
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "integer"
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer token."
          },
          "404": {
            "description": "Agent does not exist or is archived."
          }
        }
      }
    },
    "/v1/webhooks/{taskID}": {
      "post": {
        "tags": [
          "Agent webhooks"
        ],
        "summary": "Trigger an agent task via inbound webhook",
        "description": "Operator-configured webhook receiver. The customer's upstream\nsystem POSTs a JSON payload here; Sigil HMAC-verifies it against\nthe task's stored secret and starts a run with the payload as the\nobjective context.\n\nRequest must include the `X-Sigil-Signature` header containing\n`sha256=<hex-hmac>` over the raw body. Signature verification is\nconstant-time.\n\nThe `taskID` is the Sigil agent task id shown in the portal under\nthe agent's Triggers tab. Rotate the secret with the in-portal\nbutton if it leaks.\n",
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "taskID",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "in": "header",
            "name": "X-Sigil-Signature",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "HMAC-SHA256 of the raw body, formatted `sha256=<hex>`."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Free-form JSON the operator's upstream sends; Sigil passes it through to the agent's first turn as context."
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Webhook accepted; a run has been scheduled."
          },
          "401": {
            "description": "Signature missing or invalid."
          },
          "404": {
            "description": "Task not found, archived, or has webhooks disabled."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "PublishableKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Sigil-Publishable-Key",
        "description": "Publishable key (`pk_live_…`). Identifies your org from the iframe.\nSafe to ship in the browser bundle. CORS origin is validated against\nthe organization's allowed-origins list configured in the portal.\n"
      },
      "SecretKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sk_live_…",
        "description": "Secret key for server-to-server endpoints. Treat like a password.\nSupports optional IP allowlisting (CIDRs) and scopes (`full` or `read`).\nRate limited to 30 req/s sustained, 60 burst.\n"
      },
      "EndUserJWT": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "End-user JWT. TTL: 1 hour, no refresh tokens. In Sigil-hosted mode\nit's minted by `POST /v1/wallets/auth/email-otp/verify`. In OIDC mode\nit's the access token from your IdP, validated against your JWKS.\nTokens can be revoked server-side via `POST /v1/wallets/me/logout`.\n"
      },
      "SigilJWT": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Sigil-issued bearer token for the public A2A surface. Two flavours:\n\n- `ac_live_…` (\"agent caller\"): minted by a Sigil agent's runner\n  when it invokes `call_agent` against another Sigil agent. Bound\n  to (caller agent id → target agent id) and short-TTL.\n- `as_live_…` (\"agent session\"): minted by an org operator from\n  the portal for external A2A clients. Long-lived, revocable,\n  scoped to a single agent.\n\nUse the `/v1/agents/{agentID}/.well-known/agent-card.json` endpoint\nto discover the JSON-RPC URL before authenticating.\n"
      }
    },
    "parameters": {
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "Offset": {
        "name": "offset",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "default": 0
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Auth missing, invalid, or revoked.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "Conflict": {
        "description": "Conflict (e.g. wallet already exists for this curve).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests. Back off and retry.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "required": [
          "data",
          "error"
        ],
        "properties": {
          "data": {},
          "error": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/AppError"
              }
            ]
          }
        }
      },
      "ErrorEnvelope": {
        "type": "object",
        "required": [
          "data",
          "error"
        ],
        "properties": {
          "data": {
            "type": "null"
          },
          "error": {
            "$ref": "#/components/schemas/AppError"
          }
        }
      },
      "AppError": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "description": "Error codes are the API contract; messages are human-readable\nand safe to display to end users. Messages never leak internal\ndetails such as stack traces, SQL, or file paths.\n",
        "properties": {
          "code": {
            "type": "string",
            "example": "wallet_not_found"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Identity": {
        "type": "object",
        "properties": {
          "organization_id": {
            "type": "string",
            "format": "uuid"
          },
          "user_identity_id": {
            "type": "string",
            "format": "uuid"
          },
          "auth_provider": {
            "type": "string",
            "enum": [
              "sigil",
              "oidc"
            ]
          },
          "provider_user_id": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "signing_mode": {
            "type": "string",
            "enum": [
              "client",
              "tee"
            ],
            "description": "The organization's signing mode. Determines where key generation\nand signing happen. `client` = browser iframe (2-of-3 Shamir).\n`tee` = server-side Confidential Space TEE (2-of-2).\nSet at organization creation and immutable.\n"
          },
          "supported_networks": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Networks enabled for this organization (e.g. ethereum, solana, bitcoin).\nThe iframe uses this to determine which curves and keypairs to generate.\n"
          }
        }
      },
      "Wallet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "organization_id": {
            "type": "string",
            "format": "uuid"
          },
          "user_identity_id": {
            "type": "string",
            "format": "uuid"
          },
          "address": {
            "type": "string",
            "description": "Default address for this curve's primary network. Format depends\non the curve: 0x hex for secp256k1/Ethereum, base58 for ed25519/Solana,\nbech32 for Bitcoin, etc.\n"
          },
          "public_key": {
            "type": "string",
            "description": "0x-prefixed hex"
          },
          "chain_type": {
            "type": "string",
            "description": "Legacy field. Use `curve` instead."
          },
          "curve": {
            "type": "string",
            "enum": [
              "secp256k1",
              "ed25519"
            ],
            "description": "Cryptographic curve used by this wallet.\nsecp256k1: Ethereum, Bitcoin, Tron, Cosmos.\ned25519: Solana, Near, Aptos, Stellar.\n"
          },
          "networks": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Networks this wallet serves, derived from the organization's\nsupported_networks filtered by the wallet's curve.\n"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived",
              "deprecated"
            ]
          },
          "signing_mode": {
            "type": "string",
            "enum": [
              "client",
              "tee"
            ],
            "description": "Per-wallet signing mode. `client` = browser SSS reconstruction;\n`tee` = server-side reconstruction inside the attested TEE.\nSet at creation, immutable. Mirrors the Identity-level\nsigning_mode for legacy clients; new clients should rely on\nthis per-wallet field.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "account_type": {
            "type": "string",
            "enum": [
              "eoa"
            ],
            "default": "eoa",
            "description": "Always `eoa`. ERC-4337 smart accounts sit on top of an EOA in\na separate record set; see `/v1/wallets/me/smart-accounts`.\n"
          }
        }
      },
      "WalletWithIdentity": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Wallet"
          },
          {
            "type": "object",
            "properties": {
              "email": {
                "type": "string",
                "format": "email"
              },
              "provider_user_id": {
                "type": "string"
              },
              "auth_provider": {
                "type": "string",
                "enum": [
                  "sigil",
                  "oidc"
                ]
              },
              "user_last_seen_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ]
      },
      "SignRequest": {
        "type": "object",
        "required": [
          "payload",
          "encoding"
        ],
        "description": "A signing request. The TEE reconstructs the key, signs the payload,\nand wipes all material. The `curve` field selects which wallet to use\nwhen multiple curves are available.\n",
        "properties": {
          "payload": {
            "type": "string",
            "description": "The data to sign. Encoding is specified by the `encoding` field.\nFor EVM transactions, this is the unsigned transaction hash (32 bytes).\nFor arbitrary messages, this is the message bytes.\n"
          },
          "encoding": {
            "type": "string",
            "enum": [
              "hex",
              "base64"
            ],
            "description": "Encoding of the payload field."
          },
          "curve": {
            "type": "string",
            "enum": [
              "secp256k1",
              "ed25519"
            ],
            "default": "secp256k1",
            "description": "Which curve's wallet to sign with."
          },
          "chain": {
            "type": "string",
            "description": "Optional chain hint (e.g. ethereum, solana). Used for\nchain-specific signature formatting when applicable.\n"
          }
        }
      },
      "SignResponse": {
        "type": "object",
        "properties": {
          "wallet_id": {
            "type": "string",
            "format": "uuid"
          },
          "signature": {
            "type": "string",
            "description": "The produced signature in hex encoding. For secp256k1 this is\na 65-byte ECDSA signature (r + s + v). For ed25519 this is a\n64-byte EdDSA signature.\n"
          },
          "public_key": {
            "type": "string",
            "description": "0x-prefixed hex"
          }
        }
      },
      "SmartAccount": {
        "type": "object",
        "description": "An ERC-4337 LightAccount v2 record sitting on top of a Sigil EOA\nwallet, scoped to one EVM chain. The address is counterfactual\n(CREATE2 derived) until the first sponsored UserOp lands; from\nthat point on `deployed` flips to true.\n",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "wallet_id": {
            "type": "string",
            "format": "uuid",
            "description": "The owner EOA wallet."
          },
          "owner_address": {
            "type": "string",
            "description": "The owning EOA's address (the signer for UserOps)."
          },
          "chain": {
            "type": "string",
            "description": "EVM chain handle, e.g. base, arbitrum, ethereum."
          },
          "factory": {
            "type": "string",
            "enum": [
              "lightaccount_v2"
            ],
            "description": "The factory used to derive the smart-account address. Today\nonly LightAccount v2 is supported.\n"
          },
          "address": {
            "type": "string",
            "description": "The counterfactual smart-account address. Same value across\nEVM chains for the same (wallet, factory) tuple.\n"
          },
          "deployed": {
            "type": "boolean",
            "description": "False until the first UserOp on this chain succeeds (the\nbundler bundles the deployment into that op). True afterwards.\n"
          },
          "sponsor_policy": {
            "type": "string",
            "description": "Optional Pimlico sponsorship policy id bound to this record.\nEmpty when the default policy applies.\n"
          },
          "created_by": {
            "type": "string",
            "enum": [
              "user",
              "owner"
            ],
            "description": "`user` — provisioned by the end user via the wallet lane.\n`owner` — provisioned by the integrator via the S2S lane.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SignPreview": {
        "type": "object",
        "description": "Human-readable summary the wallet iframe renders on the confirm\nscreen. The integration passes one in if it has product context;\notherwise Sigil auto-generates a generic preview.\n",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "label",
                "value"
              ],
              "properties": {
                "label": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "UserOpStatus": {
        "type": "object",
        "description": "Status of a previously built UserOp. Lifecycle: `pending` →\n`submitted` (bundler accepted, not yet on-chain) → `confirmed`\nor `failed`.\n",
        "properties": {
          "user_op_hash": {
            "type": "string"
          },
          "tx_hash": {
            "type": "string",
            "description": "0x-prefixed L1/L2 tx hash, populated once the op lands."
          },
          "success": {
            "type": "boolean",
            "description": "True when status is confirmed and the on-chain execution succeeded."
          },
          "block_number": {
            "type": "string",
            "description": "0x-prefixed block number, populated once confirmed."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "submitted",
              "confirmed",
              "failed"
            ]
          }
        }
      }
    }
  }
}