> ## Documentation Index
> Fetch the complete documentation index at: https://developers.securitize.io/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> SecId API: use the sandbox base URL https://partners-api.sandbox.securitize.io/investors/v1/onboarding-reliance unless the user explicitly provides production access. Authenticate with OAuth 2.0 client credentials: POST https://partners-api.sandbox.securitize.io/oauth2/token (form fields grant_type=client_credentials, client_id, client_secret, read from environment variables), cache the bearer token for its expires_in (7200 s) and send it as Authorization: Bearer on every call; never print or log the secret or the token.
> Nothing is verified until POST /investors/{investorId}/verification/submit. An individual: create, upload one identity document, submit. A company: create, add its people as key parties (at least one with isBeneficialOwner: true), upload their documents, submit.
> Branch on the HTTP status and the error `code`, never on `message`. A body without `code` comes from the gateway: on a 401 get a new token and send the request again; a 403 means the account is not enabled for this API.
> SecId API addresses: send address.state only when that address's countryCode is US, as the 2-letter state code (NY); omit it for any other country, also when the user has a province or region. Otherwise the call answers 400 validation_error on address.state.
> SecId API individuals in the US: taxId is the person's Social Security number, and it is checked against the name and residential address sent; send them as registered. A mismatch can move the investor to manual-review instead of verified. Sandbox does not run the check.
> Creates are safe to repeat with the same body: after a timeout or a 502, send the identical create again. Document uploads are not: each call stores a new document, so only re-send a document after a 502.
> Poll GET /investors/{investorId} about every 30 seconds, then back off; treat any status you do not recognise as not verified. In sandbox, the webhook investor.kyc.updated also announces every status change, and investor.suitability.updated / investor.dlt_test.updated every change of the suitability and DLT test verificationStatus: verify the signature, deduplicate on sourceEventId and confirm with the matching GET before acting. Token whitelisting is asynchronous too: poll GET /investors/{investorId}/wallets/whitelist until the status leaves processing.
> SecId API tax forms: POST /investors/{investorId}/tax-forms takes multipart/form-data (parts file, formType, signatureDate); never base64 or JSON. Let the HTTP client set content-type with the boundary. At any point of the verification, before or after the investor is verified, only for investors you created (201), one PDF up to 10 MB per call; each upload replaces the form on file, so after a 502 send the same request again. A W-9 can instead go as JSON fields to POST /investors/{investorId}/tax-forms/w9 (TIN as 9 digits, SSN or EIN): it does not replace a form on file (409 tax_form_already_active, details.activeTaxForm), and it requires a certification object (certifiedAt, signatureName, subjectToBackupWithholding false, isUSPerson true, certificationTextVersion w9-sm-2026-10): the investor certifies in your flow on the text published at /secid-api/tax-forms#the-certification, shown without changes; subjectToBackupWithholding true answers 422 business_rule_violation (details.reason backup_withholding_not_supported). Unlike creates, a repeat after success answers 409 tax_form_already_active, not 201: when details.activeTaxForm.formType is W-9, treat it as on file.
> Use synthetic test data in sandbox (@test.invalid emails, fresh ids each run). Never send more than one DLT test attempt per test investor unless the user asks: three failed attempts lock the test permanently.
> The complete contract, with every field, response and error, is the OpenAPI file of the API reference; the Errors page says what to do for each code.
> CET Underlying API: a WebSocket at wss://partners-rt-gw.sandbox.securitize.io/v1/market-maker/stream (TLS only; ws:// hangs with no answer). Sign hello, place_order, cancel_order and submit_crypto_funding with the user's Ed25519 key, read from an environment variable or secrets store; never print or log it. Sign timestamp + "." + type + "." + the exact payload string you send: serialise the payload once and embed that same string in the frame.
> CET Underlying API: send a unique requestId of at most 32 characters (a UUID without hyphens) on every message and match replies on it; frames without a requestId are pushes. Every amount is a decimal string, never a JSON number; compare amounts as decimals. Branch on the error code, never on message.
> CET Underlying API: an ack to place_order means taken, not filled. externalRef is the idempotency key; after an ack with status UNKNOWN or an UPSTREAM_UNAVAILABLE error, call get_order by externalRef before resending. Updates are delivered at most once: after any reconnect, read working orders back with get_order. Every order is placed by quantity. A buy must be a LIMIT order: quantity × limitPrice plus fees is held against your own available cash (get_cash_balance_v2), and a buy it does not cover, a MARKET buy or a notional order is refused (INVALID_ORDER). A sell must be placed by quantity, after its CETs reach the redemption wallet, or it is refused. mint_updated and burn_updated push mints and burns once they are on chain; after a gap, read them with get_mints and get_burns. The complete contract is the AsyncAPI file of the API reference.
> Stocks API (Beta): documented ahead of its release through the partner gateway; paths, fields and errors can change. Base URL https://partners-api.sandbox.securitize.io/stocks/v1. Send x-api-key on every call and investorId on every call about an investor. Read the key from an environment variable; never print or log it, and never call the API from frontend code.
> Stocks API: an investor can trade only once the SecId API shows them verified, with verified suitability (and a passed DLT test in the EEA), and once their wallet on the stock's chain is linked with their signature of the agreement message, signed verbatim. There is no whitelisting call: the first trade whitelists the wallet.
> Stocks API: orders are fill or kill. GET /stocks/realtime-price, prepare within about 20 seconds (signedPrice), have the investor's wallet sign the returned transaction unchanged and sign-only (never send it), then submit it once with POST /stocks/execute-signed-transaction within 120 seconds. Amounts are raw base units as strings; USDC has 6 decimals. The outcome is on chain: follow transactionHash.
> Stocks API: production access requires a UI audit by Securitize Compliance of the live screens: the disclosures and the market data shown, the NBBO included.
> Corporate Actions API (Beta, 0.x): in development and subject to change; paths, fields, enumerations and event names can change before 1.0.0. Base URL https://partners-api.sandbox.securitize.io/corporate-actions/v1. Get a bearer token with OAuth 2.0 client credentials at POST https://partners-api.sandbox.securitize.io/oauth2/token (no scope, 2 hours), cache it, and send Authorization: Bearer on every call. Read the client id, secret and the webhook signing secret from environment variables; never print or log them, and never call the API from frontend code.
> Corporate Actions API: webhooks arrive signed with the Standard Webhooks scheme under the svix-id, svix-timestamp and svix-signature headers (HMAC-SHA256 over id.timestamp.raw body, base64, v1 prefix; the secret is base64 after whsec_). Verify before parsing, reject a timestamp more than 5 minutes off, answer 2xx within 10 seconds. Delivery is at least once and can be out of order: deduplicate on sourceEventId, keep the highest version per object (objectId), order by sequence per stream.id, and replace the object with data (never merge).
> Corporate Actions API: treat an unknown caType, reasonCode, eventType or enum value as informational, never as an error. A restriction ends only with ca.restriction.removed; expectedEnd is informational. marketStatus UNAVAILABLE means do not trade; the reason field carries a restriction reasonCode or a market reason such as PRE_MARKET. GET /tokens?status= filters by marketStatus (OPEN, CLOSED, UNAVAILABLE), not by the token's ACTIVE status.
> Corporate Actions API: the event routes are at https://partners-api.sandbox.securitize.io/webhooks/v1 (not under /corporate-actions/v1) and cover every webhook family sent to the partner; filter with eventTypes=ca.* for corporate actions only. After an outage call POST /events/recover with since at the start of the outage (resends failed deliveries only), or page GET /events?since= (newest first, iterator paging, 90 days), then read state with GET /tokens/{tokenId}/status and GET /corporate-actions/{sourceCaId}. In sandbox, POST /sandbox/events with a scenario and one of your tokenIds publishes a scripted sequence at once; order the events by sequence, not by arrival.
> Investor Activity API: base URL https://partners-api.sandbox.securitize.io/v1. Authentication is OAuth 2.0 client credentials only (no API key): POST https://partners-api.sandbox.securitize.io/oauth2/token (no scope, 2 hours), cache the token, and send Authorization: Bearer on every call. The token identifies the investor account; there is no investor id to pass. Read the client id, secret and the webhook signing secret from environment variables; never print or log them, and never call the API from frontend code.
> Investor Activity API: every amount is an integer string in base units with a sibling decimals (human = amount / 10^decimals); parse with BigInt or a decimal type, never a float. Cash amounts (deposits, confirmations, payouts) are in the minor units of currencyId (1 = USD, decimals 2); token amounts (transactions, mintings, holdings) in the token's base units. Never add cash to tokens.
> Investor Activity API: reconcile a subscription by grouping GET /deposits?tokenId= on tokenIssuanceId and matching the transaction in GET /transactions?tokenId= with the same tokenIssuanceId (null on a deposit means not issued yet; null on a transaction means not an issuance). Each issuance has a deposit movement (cash in) and a liquidation movement (that cash applied to the tokens) for the same amount: count the cash once, by direction=deposit, never by adding every movement. Page with offset and limit (max 100) until offset + meta.length reaches meta.total; there is no tokenIssuanceId filter. Reconcile a redemption by txHash (transfer, investor.redemption.pending, payout sourceTransaction) and by the redemption's data.id across pending, confirmed, payout.initiated and payout.completed.
> Investor Activity API: webhooks arrive signed with the Standard Webhooks scheme under the svix-id, svix-timestamp and svix-signature headers (HMAC-SHA256 over id.timestamp.raw body, base64, v1 prefix; the secret is base64 after whsec_). Verify before parsing, reject a timestamp more than 5 minutes off, answer 2xx within 15 seconds. The body is {eventType, eventId, timestamp, data}: deduplicate on eventId (not svix-id), upsert by data.id, never move a record back to an earlier step, and treat an unknown eventType, processType or paymentMethod as informational. In webhooks a field with no value is absent; in endpoint answers it is null. After an outage page GET /webhook-events (newest first, iterator paging, optional eventType) back to the start of the outage.
> SecId hosted onboarding: instead of the API, a partner can send an investor to https://stocks.securitize.io/partners?tokenId=<stock token id>&partnerUrl=<return URL> (tokenAddress may replace tokenId; partnerUrl must be an absolute https URL, URL-encoded). Securitize runs identity verification, the investment profile and the wallet there. Nothing is appended to partnerUrl on return: check the wallet on chain before trading (Beta on Solana).

# Add a document to an investor

> Uploads **one document, with all of its files**, for the investor itself. The body shape follows the investor's type, chosen by `type`:

- **Individual** → an identity document (`passport`, `driving-licence`, `national-identity-card`, `residence-permit`). A passport is one file with `side: front`; the other three are two files, one `front` and one `back`, in the same call. The file is read optically, so it must be a real, legible image (JPEG or PNG).
- **Entity** → one of its own company documents (certificate of registration, articles, shareholder register…), filed for Securitize compliance review: exactly one file, PDF allowed, no `side`, `documentNumber` or `expiresAt`. Optional: the list of types is a menu, not a checklist — send what you hold. A company has no identity document of its own: send a person's identity document to `POST /investors/{investorId}/key-parties/{keyPartyId}/documents`.

A company `type` on an individual, or an identity `type` on an entity, answers `422` before anything is stored.

**Two ways to send the files**, on this same route and with the same answers. As JSON, each file base64-encoded in `files[].content`, at most 10 MB a file. Or as `multipart/form-data`, each file as a part of its own, at most 20 MB a file: the text parts carry `type`, `documentNumber` and `expiresAt`, and the files go in a `front` part, plus a `back` part for a two-sided document, or in one `file` part for a company document. Send files over 10 MB as multipart; a part travels as the file itself, without the third that base64 adds. See [Two ways to send the files](/secid-api/documents#two-ways-to-send-the-files).

**This never starts verification.** Read `verification.status` in the response:

- individual — `awaiting_documents` while something is still owed (`missing` names it, e.g. `["IDENTITY"]`), then `awaiting_submission` once the identity document is accepted;
- entity — `awaiting_key_parties` while its key parties still need adding or documenting (`missing`, e.g. `["COMPANY_BENEFICIARIES"]`), then `awaiting_submission`.

`awaiting_submission` is not a stuck state: it is waiting for your `POST /investors/{investorId}/verification/submit`. No verdict arrives from this endpoint, and the investor's `status` stays `none`.

The status code is the outcome; there is no per-file result. A two-sided document is stored only if both sides are accepted. Each call stores a new document, so sending the same document twice keeps two copies. Once the investor is submitted, this route answers `409`. For sandbox tests you can use the sample files at `/secid-api/fixtures/passport.png` and `/secid-api/fixtures/company-document.png` on this site.




## OpenAPI

````yaml /secid-api/openapi.yaml post /investors/{investorId}/documents
openapi: 3.1.0
info:
  title: SecId API
  version: 1.0.0
  description: >
    Onboard investors you have already KYC'd. Securitize accepts your identity
    verification instead of repeating it, and runs sanctions and PEP screening
    on top; a screening flag is reviewed by Securitize compliance, not rejected
    automatically. Both journeys follow the same steps: create the investor (an
    individual, or a company with the people and companies it is verified
    through, its key parties), upload their documents, submit, and poll for the
    verdict. Every path that names an investor takes the `investorId` Securitize
    returns, not your own id; `GET /investors?externalInvestorId=…` finds it by
    yours. The guides on the developer portal explain the flow, the states and
    the error handling in depth.
servers:
  - url: >-
      https://partners-api.sandbox.securitize.io/investors/v1/onboarding-reliance
    description: Sandbox
security:
  - OAuth2: []
tags:
  - name: Authentication
    description: >
      Exchange your client id and secret for the bearer token every other
      operation takes.
  - name: Individuals
    description: >
      Create and correct a natural person you are onboarding. Nothing is under
      review until you submit the investor.
  - name: Entities
    description: >
      Create and correct the company being onboarded. A company is never
      screened on its own: it is verified through its key parties once you
      submit it.
  - name: Key parties
    description: >
      The natural persons (beneficial owners, shareholders, signers) and the
      companies (corporate shareholders) an entity is verified through. People
      are screened; companies are recorded and reviewed by Securitize
      compliance.
  - name: Documents
    description: >
      Identity documents for an individual investor or a person key party;
      company documents for a company key party or for the entity itself. One
      document per call, base64 inside JSON. Documents never start a
      verification.
  - name: Verification
    description: >-
      Hand an investor to verification. For an entity, with all of its key
      parties in one call.
  - name: Lookup
    description: >
      Read an investor's status and data back, and recover an `investorId` you
      no longer have.
  - name: Wallets
    description: >
      Register the blockchain addresses an investor holds, and list the ones you
      registered. You vouch that the investor controls each address; registering
      alone does not let a wallet receive tokens.
  - name: Suitability and DLT test
    description: >
      Send the investor's suitability profile and, for investors in the European
      Economic Area, their attempts at the DLT knowledge test. A verified
      suitability is what token whitelisting requires. An individual can send
      the compact suitability instead: the part that a Securitize Markets
      brokerage account for stocks and '40 Act funds requires.
  - name: Token whitelisting
    description: >
      Whitelist an investor's EVM wallet on a token enabled for your account, so
      it can receive and hold that token, and follow its status.
  - name: Tax forms
    description: >
      Send an investor's tax form, at any point of the verification, so it is on
      file for the withholding on the investor's distributions: the W-9, W-8BEN
      or W-8BEN-E the investor signed, as a PDF in `multipart/form-data`, or the
      fields of a W-9 as JSON.
  - name: Webhooks
    description: >
      What Securitize POSTs to the endpoint your Securitize contact registered
      for you: one event per request, signed with your endpoint's secret.
      Available in sandbox.
paths:
  /investors/{investorId}/documents:
    post:
      tags:
        - Documents
      summary: Add a document to an investor
      description: >
        Uploads **one document, with all of its files**, for the investor
        itself. The body shape follows the investor's type, chosen by `type`:


        - **Individual** → an identity document (`passport`, `driving-licence`,
        `national-identity-card`, `residence-permit`). A passport is one file
        with `side: front`; the other three are two files, one `front` and one
        `back`, in the same call. The file is read optically, so it must be a
        real, legible image (JPEG or PNG).

        - **Entity** → one of its own company documents (certificate of
        registration, articles, shareholder register…), filed for Securitize
        compliance review: exactly one file, PDF allowed, no `side`,
        `documentNumber` or `expiresAt`. Optional: the list of types is a menu,
        not a checklist — send what you hold. A company has no identity document
        of its own: send a person's identity document to `POST
        /investors/{investorId}/key-parties/{keyPartyId}/documents`.


        A company `type` on an individual, or an identity `type` on an entity,
        answers `422` before anything is stored.


        **Two ways to send the files**, on this same route and with the same
        answers. As JSON, each file base64-encoded in `files[].content`, at most
        10 MB a file. Or as `multipart/form-data`, each file as a part of its
        own, at most 20 MB a file: the text parts carry `type`, `documentNumber`
        and `expiresAt`, and the files go in a `front` part, plus a `back` part
        for a two-sided document, or in one `file` part for a company document.
        Send files over 10 MB as multipart; a part travels as the file itself,
        without the third that base64 adds. See [Two ways to send the
        files](/secid-api/documents#two-ways-to-send-the-files).


        **This never starts verification.** Read `verification.status` in the
        response:


        - individual — `awaiting_documents` while something is still owed
        (`missing` names it, e.g. `["IDENTITY"]`), then `awaiting_submission`
        once the identity document is accepted;

        - entity — `awaiting_key_parties` while its key parties still need
        adding or documenting (`missing`, e.g. `["COMPANY_BENEFICIARIES"]`),
        then `awaiting_submission`.


        `awaiting_submission` is not a stuck state: it is waiting for your `POST
        /investors/{investorId}/verification/submit`. No verdict arrives from
        this endpoint, and the investor's `status` stays `none`.


        The status code is the outcome; there is no per-file result. A two-sided
        document is stored only if both sides are accepted. Each call stores a
        new document, so sending the same document twice keeps two copies. Once
        the investor is submitted, this route answers `409`. For sandbox tests
        you can use the sample files at `/secid-api/fixtures/passport.png` and
        `/secid-api/fixtures/company-document.png` on this site.
      operationId: addInvestorDocument
      parameters:
        - $ref: '#/components/parameters/InvestorId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/IdentityDocumentRequest'
                - $ref: '#/components/schemas/CompanyDocumentRequest'
            examples:
              passport:
                summary: Passport for an individual
                value:
                  type: passport
                  documentNumber: P-IND1-0000
                  expiresAt: '2032-01-01'
                  files:
                    - mime: image/png
                      side: front
                      content: iVBORw0KGgo...
              nationalIdentityCard:
                summary: Two-sided document, both sides in one call
                value:
                  type: national-identity-card
                  documentNumber: ID-0000000
                  expiresAt: '2031-03-31'
                  files:
                    - mime: image/png
                      side: front
                      content: iVBORw0KGgo...
                    - mime: image/jpeg
                      side: back
                      content: /9j/4AAQSkZJRg...
              certificateOfRegistration:
                summary: Company document for an entity
                value:
                  type: certificate-of-registration
                  files:
                    - mime: application/pdf
                      content: JVBERi0xLjcK...
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/DocumentUpload'
            encoding:
              front:
                contentType: image/jpeg, image/png
              back:
                contentType: image/jpeg, image/png
              file:
                contentType: image/jpeg, image/png, application/pdf
            examples:
              passport:
                summary: Passport for an individual, as multipart
                value:
                  type: passport
                  documentNumber: P-IND1-0000
                  expiresAt: '2032-01-01'
                  front: (the image file)
              nationalIdentityCard:
                summary: Two-sided document, both sides in one call, as multipart
                value:
                  type: national-identity-card
                  documentNumber: ID-0000000
                  expiresAt: '2031-03-31'
                  front: (the image of the front)
                  back: (the image of the back)
              certificateOfRegistration:
                summary: Company document for an entity, as multipart
                value:
                  type: certificate-of-registration
                  file: (the PDF or image file)
      responses:
        '201':
          description: Stored. Verification did not start; read `verification.status`.
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/IndividualDocumentResult'
                  - $ref: '#/components/schemas/EntityDocumentResult'
              examples:
                individualReady:
                  summary: Individual ready (awaiting_submission)
                  value:
                    documents:
                      - id: 6ace7461ab21fd6924a19c09
                        type: passport
                        documentNumber: P-IND1-0000
                        expiresAt: '2032-01-01'
                        uploadedAt: '2026-10-13T18:11:45.540Z'
                        side: front
                    verification:
                      status: awaiting_submission
                      missing: []
                entityAwaitingSubmission:
                  summary: >-
                    Entity document filed; key parties are ready, waiting for
                    submit
                  value:
                    documents:
                      - id: 6aca84f1dc49fe8369dd1dd0
                        type: certificate-of-registration
                        uploadedAt: '2026-10-10T18:33:21.402Z'
                    verification:
                      status: awaiting_submission
                      missing: []
                entityAwaitingKeyParties:
                  summary: >-
                    Entity document filed; the entity still needs its key
                    parties
                  value:
                    documents:
                      - id: 6aca84f1dc49fe8369dd1dd0
                        type: certificate-of-registration
                        uploadedAt: '2026-10-10T18:33:21.402Z'
                    verification:
                      status: awaiting_key_parties
                      missing:
                        - COMPANY_BENEFICIARIES
        '400':
          $ref: '#/components/responses/InvalidDocument'
        '401':
          $ref: '#/components/responses/GatewayUnauthorized'
        '403':
          $ref: '#/components/responses/GatewayForbidden'
        '404':
          $ref: '#/components/responses/InvestorNotFound'
        '409':
          $ref: '#/components/responses/DocumentConflict'
        '413':
          $ref: '#/components/responses/DocumentPayloadTooLarge'
        '415':
          $ref: '#/components/responses/MimeNotSupported'
        '422':
          description: >
            - `document_rejected` — the file could not be read (corrupted,
            truncated, not a document image). Nothing was stored. Re-scan and
            send the whole document again, both sides.

            - `business_rule_violation` — the `type` does not fit this investor:
            a company document on an individual, or an identity document on an
            entity. The message names what the investor can hold.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ApiError'
                  - properties:
                      code:
                        type: string
                        enum:
                          - document_rejected
                          - business_rule_violation
              examples:
                documentRejected:
                  $ref: '#/components/examples/DocumentRejected'
                companyOnIndividual:
                  summary: Company document sent for an individual
                  value:
                    statusCode: 422
                    code: business_rule_violation
                    message: >-
                      `proof-of-address` is a company document and this investor
                      is an individual. An individual holds identity documents
                      only.
                    correlationId: partners-api-7c2e9b14-3a6d-4f81-b5e0-9d1c8a2f4e67
                identityOnEntity:
                  summary: Identity document sent for an entity
                  value:
                    statusCode: 422
                    code: business_rule_violation
                    message: >-
                      `passport` is an identity document and this investor is an
                      entity. A company has no identity document of its own —
                      send a key party's to POST
                      /investors/{investorId}/key-parties/{keyPartyId}/documents.
                    correlationId: partners-api-5d0e1b77-3f0a-4e43-9a61-2c8f0e3b9a10
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/DocumentBadGateway'
components:
  parameters:
    InvestorId:
      name: investorId
      in: path
      required: true
      description: >
        The `investorId` Securitize returned when you created the investor. Not
        your `externalInvestorId`.
      schema:
        $ref: '#/components/schemas/SecuritizeId'
  schemas:
    IdentityDocumentRequest:
      type: object
      description: >
        An identity document for an individual investor or a person key party,
        as JSON, with all of its files in one call. The whole request body is
        capped at about 31 MB (31,107,760 bytes), enough for two files at the
        maximum. The same document can also be sent as `multipart/form-data`
        (`DocumentUpload`), up to 20 MB a file.


        | `type` | files | `side` |

        |---|---|---|

        | `passport` | exactly 1 | `front` |

        | `driving-licence`, `national-identity-card`, `residence-permit` |
        exactly 2 | one `front`, one `back` |


        A wrong file count or a missing `side` answers `400 invalid_document`.
      required:
        - type
        - documentNumber
        - expiresAt
        - files
      properties:
        type:
          type: string
          enum:
            - passport
            - driving-licence
            - national-identity-card
            - residence-permit
          description: The kind of identity document.
        documentNumber:
          type: string
          minLength: 1
          description: The number printed on the document.
        expiresAt:
          $ref: '#/components/schemas/InputDate'
          description: Expiry date. Send `YYYY-MM-DD`; it is returned exactly as sent.
        files:
          type: array
          minItems: 1
          maxItems: 2
          description: >-
            The document's sides, both in the same call for a two-sided
            document.
          items:
            $ref: '#/components/schemas/IdentityFile'
    CompanyDocumentRequest:
      type: object
      description: >
        A company document, as JSON: exactly one file, PDF allowed, no `side`,
        `documentNumber` or `expiresAt`. The whole request body is capped at
        about 31 MB (31,107,760 bytes). The same document can also be sent as
        `multipart/form-data` (`DocumentUpload`), up to 20 MB.
      required:
        - type
        - files
      properties:
        type:
          $ref: '#/components/schemas/CompanyDocumentType'
        files:
          type: array
          minItems: 1
          maxItems: 1
          description: Exactly one file; more answers `400 invalid_document`.
          items:
            $ref: '#/components/schemas/CompanyFile'
    DocumentUpload:
      type: object
      description: >
        The parts of a document sent as `multipart/form-data`: the same document
        as the JSON body, with each file as a part of its own instead of base64.
        Let your HTTP client build the body and its `content-type` header, which
        carries the boundary between the parts. The name of a file part is its
        side; the file's type is read from its first bytes, so the part's
        declared type and file name do not matter. Each file is at most 20 MB.
        The document's rules are those of the JSON body: a passport is a `front`
        part; a driving licence, national identity card or residence permit is a
        `front` part and a `back` part in the same call; a company document is
        one `file` part. A part that does not belong to the document's kind, an
        empty part, or more than two files answer `400 invalid_document`;
        unknown text parts are ignored.
      required:
        - type
      properties:
        type:
          description: >-
            What the document is. An identity type takes `front` (and `back`),
            `documentNumber` and `expiresAt`; a company type takes one `file`.
          oneOf:
            - type: string
              enum:
                - passport
                - driving-licence
                - national-identity-card
                - residence-permit
            - $ref: '#/components/schemas/CompanyDocumentType'
        documentNumber:
          type: string
          description: >-
            Identity documents only, and required for them. The number printed
            on the document.
          examples:
            - P-IND1-0000
        expiresAt:
          type: string
          format: date
          description: >-
            Identity documents only, and required for them. The expiry date
            printed on the document, as `YYYY-MM-DD`.
          examples:
            - '2032-01-01'
        front:
          type: string
          format: binary
          description: >-
            Identity documents only. The front of the document (a passport's
            photo page), as a JPEG or PNG of at most 20 MB. It is read
            optically, so it must be a real, legible image.
        back:
          type: string
          format: binary
          description: >-
            Two-sided identity documents only (`driving-licence`,
            `national-identity-card`, `residence-permit`), and required for
            them. The back, as a JPEG or PNG of at most 20 MB. A passport sent
            with a `back` answers `400 invalid_document`.
        file:
          type: string
          format: binary
          description: >-
            Company documents only. The one file of the document, as a PDF, JPEG
            or PNG of at most 20 MB.
    IndividualDocumentResult:
      type: object
      description: Result of an identity document upload for an individual investor.
      required:
        - documents
        - verification
      properties:
        documents:
          type: array
          description: What is now stored for this document — one entry per side.
          items:
            $ref: '#/components/schemas/StoredDocument'
        verification:
          $ref: '#/components/schemas/IndividualReadiness'
    EntityDocumentResult:
      type: object
      description: Result of filing one of the entity's own company documents.
      required:
        - documents
        - verification
      properties:
        documents:
          type: array
          description: >-
            What is now stored — one entry, listed with the entity's own
            documents.
          items:
            $ref: '#/components/schemas/StoredDocument'
        verification:
          $ref: '#/components/schemas/EntityReadiness'
    ApiError:
      type: object
      description: >
        The error envelope of the API. Branch on `code`; `message` is for humans
        and not contractual. Quote `correlationId` to Securitize support.
      required:
        - statusCode
        - code
        - message
        - correlationId
      properties:
        statusCode:
          type: integer
          description: The HTTP status, repeated.
        code:
          type: string
          description: >
            Stable error code. Each response lists the codes it can carry and
            what to do about each.
        message:
          type: string
          description: Human-readable explanation. Not a stable contract.
        details:
          description: >
            Present on some errors. Its shape depends on the error:


            - body validation — an array of `{ field, message }`;

            - path or query validation — an array of strings;

            - `external_investor_id_conflict` — `{ investorId }`;

            - `external_key_party_id_conflict` — `{ keyPartyId }`;

            - submit refusal — `{ investor, keyParties }`, the per-subject
            accounting;

            - document refused, or not accepted while under review — `{ code,
            message }`;

            - document outcome unknown — `{ code }`, possibly empty;

            - `suitability_not_verified` — `{ notVerified }`;

            - `tax_form_type_not_allowed` — `{ allowed }`;

            - `tax_form_already_active` — `{ activeTaxForm }`;

            - DLT test locked, suitability under review or not approved, compact
            suitability for an entity, or a W-9 subject to backup withholding
            sent as data — `{ reason }`.
          anyOf:
            - $ref: '#/components/schemas/FieldErrors'
            - $ref: '#/components/schemas/MessageErrors'
            - $ref: '#/components/schemas/InvestorConflictDetails'
            - $ref: '#/components/schemas/KeyPartyConflictDetails'
            - $ref: '#/components/schemas/SubmitRefusalDetails'
            - $ref: '#/components/schemas/RefusalDetails'
            - $ref: '#/components/schemas/OutcomeUnknownDetails'
            - $ref: '#/components/schemas/SuitabilityGateDetails'
            - $ref: '#/components/schemas/AllowedTaxFormsDetails'
            - $ref: '#/components/schemas/ActiveTaxFormDetails'
            - $ref: '#/components/schemas/ReasonDetails'
        correlationId:
          type: string
          description: Identifier of this request. Quote it to Securitize support.
          examples:
            - partners-api-d3dcfc7c-a3d0-459e-98a5-9d6f23dcb998
    SecuritizeId:
      type: string
      minLength: 1
      description: >
        Identifier assigned by Securitize, opaque and specific to your partner
        account. Store it as an opaque string.
      examples:
        - 6aca84e5dc49fe8369dd1da4
    InputDate:
      type: string
      format: date
      description: '`YYYY-MM-DD`. A full ISO 8601 timestamp is also accepted.'
      examples:
        - '2015-06-30'
    IdentityFile:
      type: object
      description: One side of an identity document.
      required:
        - mime
        - side
        - content
      properties:
        mime:
          type: string
          enum:
            - image/jpeg
            - image/png
          description: >
            File type. Authoritative, and cross-checked against the file's
            leading bytes: a disagreement answers `400 invalid_document`; any
            other type (PDF included) answers `415 mime_not_supported`.
        side:
          type: string
          enum:
            - front
            - back
          description: Which side this file shows. A passport sends `front`.
        content:
          type: string
          contentEncoding: base64
          description: >
            The file, base64-encoded: at most 10 MB decoded (13,981,016
            characters of base64); more answers `413 payload_too_large`. For a
            file over 10 MB, send the document as `multipart/form-data` instead,
            up to 20 MB a file. A `data:<mime>;base64,` prefix is stripped; line
            breaks, the URL-safe alphabet and missing padding are accepted. Must
            be a real, legible image: it is read optically.
    CompanyDocumentType:
      type: string
      description: >
        The kind of company document. Three names differ from what one might
        guess: `by-laws`, `members-and-authorised-signers-list` and
        `articles-of-organization` (US spelling).
      enum:
        - articles-of-association
        - articles-of-incorporation
        - articles-of-organization
        - audited-financials
        - authorized-signers-list
        - bank-statement
        - by-laws
        - certificate-of-formation
        - certificate-of-good-standing
        - certificate-of-registration
        - constitutional-document
        - evidence-of-ein
        - foundation-charter
        - list-of-officers-and-directors
        - members-and-authorised-signers-list
        - memorandum-of-association
        - non-profit-bylaws
        - offering-agreement
        - operating-agreement
        - operational-address
        - org-chart
        - ownership-designation-letter
        - partnership-agreement
        - private-placement-memorandum
        - proof-of-address
        - prospectus
        - register-of-partners
        - shareholders-list-10-percent-ubo-control
        - shareholders-list-25-percent-ubo-control
        - trust-agreement
        - w-8-ben-e
        - w-9
    CompanyFile:
      type: object
      description: The single file of a company document.
      required:
        - mime
        - content
      properties:
        mime:
          type: string
          enum:
            - image/jpeg
            - image/png
            - application/pdf
          description: >
            File type. Authoritative, and cross-checked against the file's
            leading bytes: a disagreement answers `400 invalid_document`; any
            other type answers `415 mime_not_supported`.
        content:
          type: string
          contentEncoding: base64
          description: >
            The file, base64-encoded: at most 10 MB decoded (13,981,016
            characters of base64); more answers `413 payload_too_large`. For a
            file over 10 MB, send the document as `multipart/form-data` instead,
            up to 20 MB; beyond that, reduce the scan rather than splitting it —
            one call takes one file. A `data:<mime>;base64,` prefix is stripped;
            line breaks, the URL-safe alphabet and missing padding are accepted.
    StoredDocument:
      type: object
      description: >
        A document Securitize holds. Identity documents carry `side` (a passport
        reads `front`); a two-sided one is two entries, one per side, sharing a
        `documentNumber`. Company documents carry only `id`, `type` and
        `uploadedAt`.
      required:
        - id
        - type
        - uploadedAt
      properties:
        id:
          $ref: '#/components/schemas/SecuritizeId'
          description: Securitize's id for this stored file.
        type:
          type: string
          description: The document type sent, identity or company.
        side:
          type: string
          enum:
            - front
            - back
          description: Identity documents only — which side this entry is.
        documentNumber:
          type: string
          description: Identity documents only.
        expiresAt:
          type: string
          description: Identity documents only — expiry date, exactly as sent.
        uploadedAt:
          type: string
          format: date-time
          description: When the document was stored.
    IndividualReadiness:
      type: object
      description: Whether the individual has what verification needs.
      required:
        - status
        - missing
      properties:
        status:
          type: string
          enum:
            - awaiting_documents
            - awaiting_submission
            - submitted
          description: >
            `awaiting_documents` — something is still owed; see `missing`.
            `awaiting_submission` — ready; nothing happens until your submit.
            `submitted` — the investor was already submitted and Securitize
            asked for more information: the document was added to the investor's
            file for review.
        missing:
          type: array
          description: What is still owed, e.g. `["IDENTITY"]`. Empty when ready.
          items:
            type: string
    EntityReadiness:
      type: object
      description: Where the entity stands after one of its own company documents is filed.
      required:
        - status
        - missing
      properties:
        status:
          type: string
          enum:
            - awaiting_key_parties
            - awaiting_submission
          description: >
            `awaiting_key_parties` — the entity still needs its key parties
            added and documented; see `missing`. `awaiting_submission` — nothing
            outstanding; waiting for your submit.
        missing:
          type: array
          description: What the entity still waits for, e.g. `["COMPANY_BENEFICIARIES"]`.
          items:
            type: string
    TokenError:
      type: object
      additionalProperties: false
      description: >
        The gateway's answer to a missing, invalid or expired bearer token, in
        the OAuth 2.0 shape: `error` and `error_description`, no `code` and no
        `correlationId`. The request never reached the API.
      required:
        - error
        - error_description
      properties:
        error:
          type: string
          enum:
            - invalid_request
            - invalid_token
          description: >
            `invalid_request`: no `Authorization: Bearer` header.
            `invalid_token`: the token is unknown or has expired.
        error_description:
          type: string
    GatewayError:
      type: object
      additionalProperties: false
      description: >
        The gateway's error shape: only `message`, no `code` and no
        `correlationId`. It means the request never reached the API — check the
        account or the path, not the payload.
      required:
        - message
      properties:
        message:
          type: string
          description: >
            `You cannot consume this service` (`403`: your account is not
            enabled for this API), `IP address not allowed: <ip>` (`403`: a path
            or method outside the API).
    FieldErrors:
      type: array
      description: Body validation — one entry per offending field.
      items:
        type: object
        required:
          - field
          - message
        properties:
          field:
            type: string
            description: >-
              Path of the field, with dots and indexes, e.g. `phone.code` or
              `files.0.content`.
          message:
            type: string
            description: What is wrong with it.
    MessageErrors:
      type: array
      description: Path or query validation — one message per problem.
      items:
        type: string
    InvestorConflictDetails:
      type: object
      description: The investor that already holds the identifiers sent.
      required:
        - investorId
      properties:
        investorId:
          $ref: '#/components/schemas/SecuritizeId'
          description: The existing investor's id. Use it instead of creating another.
    KeyPartyConflictDetails:
      type: object
      description: The key party that already holds the `externalKeyPartyId` sent.
      required:
        - keyPartyId
      properties:
        keyPartyId:
          $ref: '#/components/schemas/SecuritizeId'
          description: The existing key party's id.
    SubmitRefusalDetails:
      type: object
      description: Per-subject accounting of a refused submit. Nothing was submitted.
      required:
        - investor
        - keyParties
      properties:
        investor:
          $ref: '#/components/schemas/SubmissionOutcome'
          description: The investor itself.
        keyParties:
          type: array
          description: >-
            Each key party of an entity; the ones not ready read `failed` with a
            reason. Empty for an individual.
          items:
            $ref: '#/components/schemas/SubmissionOutcome'
    RefusalDetails:
      type: object
      description: The verification side's own refusal of a document.
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: >-
            Refusal code, for example `corrupted-file`. Not a fixed list; read
            `message`.
        message:
          type: string
          description: Human-readable explanation.
    OutcomeUnknownDetails:
      type: object
      description: A document whose outcome is unknown. May be empty.
      properties:
        code:
          type: string
          description: Reason code, when there is one.
    SuitabilityGateDetails:
      type: object
      description: Which suitability is not verified yet.
      required:
        - notVerified
      properties:
        notVerified:
          type: array
          description: '`us` (the suitability) and/or `eu` (the DLT test).'
          items:
            type: string
            enum:
              - us
              - eu
    AllowedTaxFormsDetails:
      type: object
      description: The tax forms this investor can receive.
      required:
        - allowed
      properties:
        allowed:
          type: array
          items:
            $ref: '#/components/schemas/TaxFormType'
    ActiveTaxFormDetails:
      type: object
      description: The tax form the investor has on file.
      required:
        - activeTaxForm
      properties:
        activeTaxForm:
          type: object
          required:
            - taxFormId
          properties:
            taxFormId:
              type: string
              description: Securitize's identifier for the form on file.
            formType:
              $ref: '#/components/schemas/TaxFormType'
    ReasonDetails:
      type: object
      description: A machine-readable reason.
      required:
        - reason
      properties:
        reason:
          type: string
          description: >-
            For example `dlt_test_locked`, `suitability_under_review` or
            `investor_type_not_supported`. Each operation lists the reasons it
            can answer.
    SubmissionOutcome:
      type: object
      description: >-
        What happened to one subject — the entity itself or one of its key
        parties.
      required:
        - subjectId
        - result
      properties:
        subjectId:
          $ref: '#/components/schemas/SecuritizeId'
          description: The `investorId` for the investor itself, or a `keyPartyId`.
        result:
          type: string
          enum:
            - submitted
            - skipped
            - failed
            - not_attempted
          description: >
            `submitted` — handed over on this call (a company key party:
            recorded for compliance review). `skipped` — already handed over,
            not re-sent; this makes repeats safe. `failed` — attempted and
            refused; see `reason`. `not_attempted` — never reached; a repeat
            sends exactly these.
        reason:
          $ref: '#/components/schemas/SubmissionReason'
    TaxFormType:
      type: string
      description: >
        The form in the file. `W-9` for a US person, individual or entity;
        `W-8BEN` for an individual who is not a US person; `W-8BEN-E` for an
        entity that is not a US person.
      enum:
        - W-9
        - W-8BEN
        - W-8BEN-E
    SubmissionReason:
      type: object
      description: >
        Why a subject was not submitted. Branch on `code`; `message` is for
        humans and not contractual. Known values: `already-handed-over` (already
        with verification, not re-sent), `documents-incomplete` (something is
        still owed), `provider-rate-limited` and `submission-failed` (the
        hand-over did not go through; call submit again),
        `key-parties-not-ready` (an entity waits for its key parties). Treat any
        other value as "not submitted; read `message`".
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: Stable reason code.
        message:
          type: string
          description: Human-readable explanation.
  responses:
    InvalidDocument:
      description: >
        - `invalid_document` — the document is incomplete or contradicts itself:
        a missing or unknown `type`, wrong number of files, a missing or wrong
        `side`, a missing `documentNumber` or `expiresAt`, `content` that is not
        base64, or `mime` disagreeing with the file's bytes. Fix the metadata,
        not the image. Nothing was stored.

        - `invalid_document` on a `multipart/form-data` body — a file part that
        does not belong to the document's kind (an identity document takes
        `front` and `back`, a company document `file`), an empty file part, more
        than two files, a part with an unknown name, or a body that cannot be
        read as multipart (a `content-type` set by hand without the boundary the
        HTTP client generated). `details` names the part when it can (`front`,
        `back`, `file`; `front|back` when no file came). Let the HTTP client
        build the body and its header.

        - `validation_error` — the body is malformed otherwise. `details` names
        each field.
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/ApiError'
              - properties:
                  code:
                    type: string
                    enum:
                      - invalid_document
                      - validation_error
          examples:
            passportSide:
              summary: A passport needs side front
              value:
                statusCode: 400
                code: invalid_document
                message: Validation failed
                details:
                  - field: type
                    message: passport file side must be "front"
                correlationId: partners-api-a4fa87b8-903c-40fb-a0d6-6652cefb1a52
            mimeMismatch:
              summary: mime disagrees with the file bytes
              value:
                statusCode: 400
                code: invalid_document
                message: Validation failed
                details:
                  - field: files.0.content
                    message: >-
                      content is not a valid image/png file — its leading bytes
                      do not match the declared mime
                correlationId: partners-api-d93eabb3-797c-45e2-880e-0ff8be58ef31
            companyTooManyFiles:
              summary: A company document takes exactly one file
              value:
                statusCode: 400
                code: invalid_document
                message: Validation failed
                details:
                  - field: files
                    message: files must contain no more than 1 elements
                correlationId: partners-api-a32f903f-18ae-4356-bea2-0a80b8319db5
            multipartMissingBack:
              summary: Multipart — a two-sided document without its back part
              value:
                statusCode: 400
                code: invalid_document
                message: Validation failed
                details:
                  - field: type
                    message: driving-licence requires exactly 2 files (front and back)
                correlationId: partners-api-efbbb191-aded-4cbf-8363-e7b108b7ae9a
            multipartWrongPart:
              summary: Multipart — an identity document sent in a file part
              value:
                statusCode: 400
                code: invalid_document
                message: >-
                  An identity document is sent as a `front` part and, for
                  two-sided types, a `back` part.
                details:
                  - field: file
                    message: >-
                      is not a file part of this document type; expected: front,
                      back
                correlationId: partners-api-a4887012-62df-430e-8cf3-2abf3fe67397
            multipartEmptyPart:
              summary: Multipart — an empty file part
              value:
                statusCode: 400
                code: invalid_document
                message: A file part is empty.
                details:
                  - field: front
                    message: is empty
                correlationId: partners-api-1898547c-7c47-4c73-bbc4-cd276d96aa1b
            multipartUnreadable:
              summary: >-
                Multipart — a part with an unknown name, or a content-type
                without its boundary
              value:
                statusCode: 400
                code: invalid_document
                message: >-
                  The multipart body is not a valid document upload (Unexpected
                  field). Send the files as a `front` part and, for two-sided
                  identity documents, a `back` part; a company document as one
                  `file` part.
                correlationId: partners-api-542df635-9243-4acc-bd7c-d6bf50ced07e
    GatewayUnauthorized:
      description: >
        Refused by the gateway: no bearer token, or one that is unknown or has
        expired. The request never reached the API. Get a new token and send the
        request again.
      headers:
        WWW-Authenticate:
          description: >-
            `Bearer realm="service"`, with `error` and `error_description` when
            a token was sent.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/TokenError'
          examples:
            missing:
              summary: No Authorization header
              value:
                error_description: The access token is missing
                error: invalid_request
            invalid:
              summary: Unknown or expired token
              value:
                error_description: The access token is invalid or has expired
                error: invalid_token
    GatewayForbidden:
      description: >
        Refused by the gateway: the token is valid but your account is not
        enabled for this API. The request never reached the API. Contact
        Securitize to have it enabled.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/GatewayError'
          examples:
            notEnabled:
              summary: Account not enabled for this API
              value:
                message: You cannot consume this service
    InvestorNotFound:
      description: >
        `investor_not_found` — no investor with this id in your account: a typo,
        an id of another partner, an id of the other investor type (an
        individual where an entity is expected), or your `externalInvestorId`
        where Securitize's `investorId` is expected. Recover the `investorId`
        with `GET /investors?externalInvestorId=…`.
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/ApiError'
              - properties:
                  code:
                    type: string
                    enum:
                      - investor_not_found
          examples:
            notFound:
              summary: Unknown investor
              value:
                statusCode: 404
                code: investor_not_found
                message: Investor not found
                correlationId: partners-api-d8899b8a-ff77-4819-a2f0-4db50be1f634
    DocumentConflict:
      description: >
        - `verification_in_review` — the investor was submitted and is under
        review; it takes no new documents. Nothing was stored. If Securitize
        needs anything more, it tells you.

        - `verification_already_settled` — the verification was decided.
        Terminal.

        - `verification_not_initialised` — the registration for verification of
        this investor (or the key party, or its entity) did not complete. Send
        that create again with the same data, then send the document again.

        - `investor_not_owned` — another party leads this investor's
        verification.

        - `investor_not_eligible`, `verification_refused` — the document cannot
        be applied to this verification. Contact Securitize support with the
        `correlationId`.
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/ApiError'
              - properties:
                  code:
                    type: string
                    enum:
                      - verification_in_review
                      - verification_already_settled
                      - verification_not_initialised
                      - investor_not_owned
                      - investor_not_eligible
                      - investor_type_mismatch
                      - verification_refused
          examples:
            inReview:
              summary: Under review; no new documents
              value:
                statusCode: 409
                code: verification_in_review
                message: >-
                  This investor's verification is in progress and takes no new
                  documents while it runs. Nothing was stored for this request;
                  follow the investor's status for the outcome.
                details:
                  code: applicant-not-accepting-documents
                  message: already under review
                correlationId: partners-api-a3f4c83d-c170-4513-8bbf-9651a02d982b
            settled:
              $ref: '#/components/examples/InvestorSettled'
            notOwned:
              $ref: '#/components/examples/InvestorNotOwned'
    DocumentPayloadTooLarge:
      description: >
        `payload_too_large` — a file is over its limit, or the JSON body is over
        about 31 MB. Refused whole; nothing was stored. As JSON, one file is
        over 10 MB (its base64 `content` over 13,981,016 characters): send the
        document as `multipart/form-data` instead, up to 20 MB a file. As
        multipart, one file part is over 20 MB: reduce the file. A file is
        refused only after it has been uploaded, so check its size before
        sending. A body far over the limit can be refused by the gateway first,
        in the bare `{ message }` shape.
      content:
        application/json:
          schema:
            oneOf:
              - allOf:
                  - $ref: '#/components/schemas/ApiError'
                  - properties:
                      code:
                        type: string
                        enum:
                          - payload_too_large
              - $ref: '#/components/schemas/GatewayError'
          examples:
            tooLarge:
              summary: Body over the limit
              value:
                statusCode: 413
                code: payload_too_large
                message: Payload too large
                correlationId: partners-api-28aaa856-786d-4275-8faf-c94a17f157f2
            fileTooLarge:
              summary: One file over 10 MB
              value:
                statusCode: 413
                code: payload_too_large
                message: Validation failed
                details:
                  - field: files.0.content
                    message: content must be no larger than 13981016 bytes
                correlationId: partners-api-9b3e1d7a-4c2f-4e86-a1d5-7f0c3b9e2a64
            filePartTooLarge:
              summary: Multipart — one file part over 20 MB
              value:
                statusCode: 413
                code: payload_too_large
                message: A file part is larger than 20 MB.
                correlationId: partners-api-6b30c8c8-6e9d-42e0-8ced-1870e7cf622c
    MimeNotSupported:
      description: >
        `mime_not_supported` — the file type is not accepted on this document
        (identity documents take `image/jpeg` and `image/png`; company documents
        also take `application/pdf`; convert the file), or the request's
        `Content-Type` is neither `application/json` nor `multipart/form-data`.
        In a multipart body the type is read from the file's first bytes, and
        `details` names the part (`front.mime`, `back.mime`, `file.mime`).
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/ApiError'
              - properties:
                  code:
                    type: string
                    enum:
                      - mime_not_supported
          examples:
            pdfOnIdentity:
              summary: PDF sent as an identity document
              value:
                statusCode: 415
                code: mime_not_supported
                message: Validation failed
                details:
                  - field: files.0.mime
                    message: 'mime must be one of: image/jpeg, image/png'
                correlationId: partners-api-fdcb9df4-45b1-4251-84a0-9d284a484181
            multipartPdfOnIdentity:
              summary: Multipart — a PDF in the front part of an identity document
              value:
                statusCode: 415
                code: mime_not_supported
                message: Validation failed
                details:
                  - field: front.mime
                    message: 'mime must be one of: image/jpeg, image/png'
                correlationId: partners-api-4930d1ca-e1e3-439f-8692-7273dc8121ed
            otherContentType:
              summary: A body that is neither JSON nor multipart/form-data
              value:
                statusCode: 415
                code: mime_not_supported
                message: >-
                  Content-Type must be application/json or multipart/form-data
                  (received: "text/plain")
                correlationId: partners-api-7284b5bf-973b-42bb-899d-d450fbc61c72
    InternalError:
      description: >
        `internal_error` — an unexpected fault on Securitize's side. The message
        is always generic. Retry; if it persists, send the `correlationId` to
        Securitize support.
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/ApiError'
              - properties:
                  code:
                    type: string
                    enum:
                      - internal_error
          examples:
            internal:
              summary: Internal error
              value:
                statusCode: 500
                code: internal_error
                message: Internal error
                correlationId: partners-api-5b0c7a1e-9d3f-4e2a-8c6b-1f4d2e3a9b70
    DocumentBadGateway:
      description: >
        A `502` on a document does not always mean nothing happened:


        - `file_upload_failed` — nothing was stored. Send it again.

        - `document_outcome_unknown` — **the document was kept**, but whether
        verification accepted it is not known. Send it again: that is safe, and
        the extra copy is harmless.

        - `verification_handoff_failed` — the verification service could not be
        reached. **The document was kept**; send it again.
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/ApiError'
              - properties:
                  code:
                    type: string
                    enum:
                      - file_upload_failed
                      - document_outcome_unknown
                      - verification_handoff_failed
          examples:
            outcomeUnknown:
              summary: Document kept, outcome unknown
              value:
                statusCode: 502
                code: document_outcome_unknown
                message: >-
                  No verdict came back for this document, so whether the
                  identity provider accepted it is not known. It is stored on
                  our side and has NOT been discarded. Send it again — that is
                  safe, and may leave a duplicate at the provider, which is
                  harmless.
                correlationId: partners-api-3d9a1f7b-6c2e-4b8a-a5d0-e4f3c2b1a098
  examples:
    DocumentRejected:
      summary: The image could not be read
      value:
        statusCode: 422
        code: document_rejected
        message: >-
          The identity provider did not accept this document: Error analyzing
          file, unsupported format or corrupted file. Nothing was stored for it
          — correct it and send the whole document again, both sides.
        details:
          code: corrupted-file
          message: Error analyzing file, unsupported format or corrupted file
        correlationId: partners-api-936e9d6c-c7d7-4e10-8116-79b4c263fabe
    InvestorSettled:
      summary: Verification already decided
      value:
        statusCode: 409
        code: verification_already_settled
        message: >-
          This investor's verification is already resolved, so its data and
          documents can no longer change.
        correlationId: partners-api-84f3039c-2995-490e-bd2f-f25736aeafad
    InvestorNotOwned:
      summary: Limited access (investor_not_owned)
      value:
        statusCode: 409
        code: investor_not_owned
        message: >-
          This investor's verification is led by another party; you can read its
          status and manage its wallets, token whitelisting, suitability and DLT
          test, but not change its data or documents.
        correlationId: partners-api-9599b8a2-d164-5cb3-b057-2019ae3b8e87
  securitySchemes:
    OAuth2:
      type: oauth2
      description: >
        OAuth 2.0 client credentials. Exchange the client id and secret
        Securitize issued you for a bearer token at the token URL (the host
        root, not under the base URL), with `grant_type=client_credentials`; the
        token lasts 2 hours and goes in the `Authorization: Bearer` header of
        every request. No scope is needed. The token identifies your partner
        account, so you never send a partner id, and every record you create is
        scoped to you.
      flows:
        clientCredentials:
          tokenUrl: https://partners-api.sandbox.securitize.io/oauth2/token
          scopes: {}

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.