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

# Request to vault a unit

> Records the owner's approval to place the unit's item with a vault. Authorized by the owner's tap session token. The request stays `pending` until the vault confirms it has the item, and expires after 30 days. Send an `Idempotency-Key` header to make a retry safe.



## OpenAPI

````yaml /openapi.json post /v1/units/{unit_id}/vault-requests
openapi: 3.1.0
info:
  title: Endstate API
  version: 0.1.0
  description: Endstate developer API for chip verification and ownership workflows.
servers:
  - url: https://api2.endstate.io
    description: Production
  - url: https://api-staging.endstate.io
    description: Staging
security: []
paths:
  /v1/units/{unit_id}/vault-requests:
    post:
      tags:
        - Vaults
      summary: Request to vault a unit
      description: >-
        Records the owner's approval to place the unit's item with a vault.
        Authorized by the owner's tap session token. The request stays `pending`
        until the vault confirms it has the item, and expires after 30 days.
        Send an `Idempotency-Key` header to make a retry safe.
      operationId: createVaultRequest
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: unit_id
          in: path
        - schema:
            type: string
            minLength: 1
            maxLength: 255
            description: >-
              Unique key that makes this request safe to retry. Replaying the
              key with the same body returns the original response with
              `Idempotent-Replayed: true`; replaying it with a different body
              returns 409 `idempotency.key_conflict`.
            example: a1b2c3d4-0000-4000-8000-000000000000
          required: false
          description: >-
            Unique key that makes this request safe to retry. Replaying the key
            with the same body returns the original response with
            `Idempotent-Replayed: true`; replaying it with a different body
            returns 409 `idempotency.key_conflict`.
          name: Idempotency-Key
          in: header
      requestBody:
        content:
          application/json:
            example:
              vault_id: 33333333-3333-4333-8333-333333333333
              nonce: >-
                0x1111111111111111111111111111111111111111111111111111111111111111
              owner_signature: >-
                0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            schema:
              $ref: '#/components/schemas/CreateVaultRequest'
      responses:
        '201':
          description: The pending vault request.
          content:
            application/json:
              example:
                id: 00000000-0000-4000-8000-000000000000
                unit_id: 22222222-2222-4222-8222-222222222222
                vault:
                  id: 33333333-3333-4333-8333-333333333333
                  name: Alliance Vault
                  logo_url: https://vault.example/logo.png
                  key_mode: managed
                status: pending
                notes: null
                rejection_reason: null
                decided_at: null
                available_at: null
                hold: null
                ready_to_ship: false
                dispatched_at: null
                expires_at: '2026-07-15T12:00:00.000Z'
                created_at: '2026-06-15T12:00:00.000Z'
              schema:
                $ref: '#/components/schemas/VaultRequest'
        '400':
          description: >-
            The request was malformed or failed validation.


            | Error code | When |

            | --- | --- |

            | `validation.failed` | The request failed schema validation. See
            `error.details` for per-field issues. |
          x-error-codes:
            - validation.failed
          content:
            application/json:
              examples:
                validation.failed:
                  summary: >-
                    The request failed schema validation. See `error.details`
                    for per-field issues.
                  value:
                    error:
                      code: validation.failed
                      message: >-
                        The request failed schema validation. See
                        `error.details` for per-field issues.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/validation-failed
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: >-
            Authentication failed.


            | Error code | When |

            | --- | --- |

            | `auth.unauthorized` | Credential is missing or malformed. |

            | `session_token.invalid_or_expired` | The session token is unknown,
            expired, or has been revoked. |
          x-error-codes:
            - auth.unauthorized
            - session_token.invalid_or_expired
          content:
            application/json:
              examples:
                auth.unauthorized:
                  summary: Credential is missing or malformed.
                  value:
                    error:
                      code: auth.unauthorized
                      message: Credential is missing or malformed.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/auth-unauthorized
                session_token.invalid_or_expired:
                  summary: The session token is unknown, expired, or has been revoked.
                  value:
                    error:
                      code: session_token.invalid_or_expired
                      message: >-
                        The session token is unknown, expired, or has been
                        revoked.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: >-
                        https://docs.endstate.io/errors/session-token-invalid-or-expired
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: >-
            Authenticated, but not permitted.


            | Error code | When |

            | --- | --- |

            | `auth.forbidden` | Credential is valid but does not have access to
            the requested resource or action. |

            | `session_token.wrong_chip` | The session token is bound to a
            different chip than the one in the request. |

            | `vault.consent_invalid` | The owner's approval is not signed by
            the unit's current owner for this vault and nonce. Sign it again. |

            | `vault.not_permitted` | The environment may not use this vault: it
            has no allowed connection to it, or the vault belongs to your own
            organization. |
          x-error-codes:
            - auth.forbidden
            - session_token.wrong_chip
            - vault.consent_invalid
            - vault.not_permitted
          content:
            application/json:
              examples:
                auth.forbidden:
                  summary: >-
                    Credential is valid but does not have access to the
                    requested resource or action.
                  value:
                    error:
                      code: auth.forbidden
                      message: >-
                        Credential is valid but does not have access to the
                        requested resource or action.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/auth-forbidden
                session_token.wrong_chip:
                  summary: >-
                    The session token is bound to a different chip than the one
                    in the request.
                  value:
                    error:
                      code: session_token.wrong_chip
                      message: >-
                        The session token is bound to a different chip than the
                        one in the request.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/session-token-wrong-chip
                vault.consent_invalid:
                  summary: >-
                    The owner's approval is not signed by the unit's current
                    owner for this vault and nonce. Sign it again.
                  value:
                    error:
                      code: vault.consent_invalid
                      message: >-
                        The owner's approval is not signed by the unit's current
                        owner for this vault and nonce. Sign it again.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/vault-consent-invalid
                vault.not_permitted:
                  summary: >-
                    The environment may not use this vault: it has no allowed
                    connection to it, or the vault belongs to your own
                    organization.
                  value:
                    error:
                      code: vault.not_permitted
                      message: >-
                        The environment may not use this vault: it has no
                        allowed connection to it, or the vault belongs to your
                        own organization.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/vault-not-permitted
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: >-
            The resource does not exist within your organization.


            | Error code | When |

            | --- | --- |

            | `not_found.resource` | The requested resource does not exist
            within your organization. |

            | `unit.not_found` | The unit ID does not exist in your
            organization. |

            | `vault.not_found` | No vault with that ID is visible to your
            environment. |
          x-error-codes:
            - not_found.resource
            - unit.not_found
            - vault.not_found
          content:
            application/json:
              examples:
                not_found.resource:
                  summary: >-
                    The requested resource does not exist within your
                    organization.
                  value:
                    error:
                      code: not_found.resource
                      message: >-
                        The requested resource does not exist within your
                        organization.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/not-found-resource
                unit.not_found:
                  summary: The unit ID does not exist in your organization.
                  value:
                    error:
                      code: unit.not_found
                      message: The unit ID does not exist in your organization.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/unit-not-found
                vault.not_found:
                  summary: No vault with that ID is visible to your environment.
                  value:
                    error:
                      code: vault.not_found
                      message: No vault with that ID is visible to your environment.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/vault-not-found
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >-
            The request conflicts with the resource's current state.


            | Error code | When |

            | --- | --- |

            | `collection.not_active` | The collection has not finished
            provisioning. Wait until its status is `active`. |

            | `idempotency.in_progress` | A request with this `Idempotency-Key`
            is already in progress. |

            | `idempotency.key_conflict` | The `Idempotency-Key` was previously
            used with a different request body. |

            | `unit.not_minted` | The unit has not been issued yet. Pair a chip
            and wait for `token.status: "active"` first. |

            | `vault.consent_used` | The owner's approval was already used or
            cancelled. The owner signs a new one with a fresh `nonce`. |

            | `vault.in_progress` | The unit already has an open vault request
            or is already vaulted. An open request ends when the vault confirms
            or rejects it, its owner cancels it, or it expires; vault
            eligibility's `request_id` names it while the owner can still
            cancel. A vaulted unit takes no new request until it is unvaulted. |

            | `vault.unavailable` | The vault takes no new items. Either the
            connection to it is not active, or the vault is not active because
            it is still provisioning, Endstate has suspended or retired it, or
            its environment is archived. A vault that is not active also cannot
            invite a brand or have a connection accepted.
            `error.details.vault_id` names the vault. |
          x-error-codes:
            - collection.not_active
            - idempotency.in_progress
            - idempotency.key_conflict
            - unit.not_minted
            - vault.consent_used
            - vault.in_progress
            - vault.unavailable
          content:
            application/json:
              examples:
                collection.not_active:
                  summary: >-
                    The collection has not finished provisioning. Wait until its
                    status is `active`.
                  value:
                    error:
                      code: collection.not_active
                      message: >-
                        The collection has not finished provisioning. Wait until
                        its status is `active`.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/collection-not-active
                idempotency.in_progress:
                  summary: >-
                    A request with this `Idempotency-Key` is already in
                    progress.
                  value:
                    error:
                      code: idempotency.in_progress
                      message: >-
                        A request with this `Idempotency-Key` is already in
                        progress.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/idempotency-in-progress
                idempotency.key_conflict:
                  summary: >-
                    The `Idempotency-Key` was previously used with a different
                    request body.
                  value:
                    error:
                      code: idempotency.key_conflict
                      message: >-
                        The `Idempotency-Key` was previously used with a
                        different request body.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/idempotency-key-conflict
                unit.not_minted:
                  summary: >-
                    The unit has not been issued yet. Pair a chip and wait for
                    `token.status: "active"` first.
                  value:
                    error:
                      code: unit.not_minted
                      message: >-
                        The unit has not been issued yet. Pair a chip and wait
                        for `token.status: "active"` first.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/unit-not-minted
                vault.consent_used:
                  summary: >-
                    The owner's approval was already used or cancelled. The
                    owner signs a new one with a fresh `nonce`.
                  value:
                    error:
                      code: vault.consent_used
                      message: >-
                        The owner's approval was already used or cancelled. The
                        owner signs a new one with a fresh `nonce`.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/vault-consent-used
                vault.in_progress:
                  summary: >-
                    The unit already has an open vault request or is already
                    vaulted. An open request ends when the vault confirms or
                    rejects it, its owner cancels it, or it expires; vault
                    eligibility's `request_id` names it while the owner can
                    still cancel. A vaulted unit takes no new request until it
                    is unvaulted.
                  value:
                    error:
                      code: vault.in_progress
                      message: >-
                        The unit already has an open vault request or is already
                        vaulted. An open request ends when the vault confirms or
                        rejects it, its owner cancels it, or it expires; vault
                        eligibility's `request_id` names it while the owner can
                        still cancel. A vaulted unit takes no new request until
                        it is unvaulted.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/vault-in-progress
                vault.unavailable:
                  summary: >-
                    The vault takes no new items. Either the connection to it is
                    not active, or the vault is not active because it is still
                    provisioning, Endstate has suspended or retired it, or its
                    environment is archived. A vault that is not active also
                    cannot invite a brand or have a connection accepted.
                    `error.details.vault_id` names the vault.
                  value:
                    error:
                      code: vault.unavailable
                      message: >-
                        The vault takes no new items. Either the connection to
                        it is not active, or the vault is not active because it
                        is still provisioning, Endstate has suspended or retired
                        it, or its environment is archived. A vault that is not
                        active also cannot invite a brand or have a connection
                        accepted. `error.details.vault_id` names the vault.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/vault-unavailable
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: |-
            Too many requests.

            | Error code | When |
            | --- | --- |
            | `rate_limit.exceeded` | Per-key rate limit exceeded. |
          x-error-codes:
            - rate_limit.exceeded
          content:
            application/json:
              examples:
                rate_limit.exceeded:
                  summary: Per-key rate limit exceeded.
                  value:
                    error:
                      code: rate_limit.exceeded
                      message: Per-key rate limit exceeded.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/rate-limit-exceeded
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: >-
            Something went wrong on our end.


            | Error code | When |

            | --- | --- |

            | `internal.error` | An unexpected server error occurred. Retry with
            exponential backoff and include `request_id` in any support request.
            |
          x-error-codes:
            - internal.error
          content:
            application/json:
              examples:
                internal.error:
                  summary: >-
                    An unexpected server error occurred. Retry with exponential
                    backoff and include `request_id` in any support request.
                  value:
                    error:
                      code: internal.error
                      message: >-
                        An unexpected server error occurred. Retry with
                        exponential backoff and include `request_id` in any
                        support request.
                      request_id: req_8e1a7f50-90ab-4cde-f012-3456789abcde
                      doc_url: https://docs.endstate.io/errors/internal-error
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - SessionTokenBearer: []
components:
  schemas:
    CreateVaultRequest:
      type: object
      properties:
        vault_id:
          type: string
          format: uuid
          description: >-
            The vault to hold the item. It must be the brand's default vault or
            another vault the brand allows.
        nonce:
          type: string
          pattern: ^0x[0-9a-fA-F]{64}$
          description: The 32-byte nonce in the owner's signed approval.
        owner_signature:
          type: string
          pattern: ^0x(?:[0-9a-fA-F]{2})+$
          description: The owner's signature over the approval.
      required:
        - vault_id
        - nonce
        - owner_signature
      additionalProperties: false
    VaultRequest:
      type: object
      properties:
        id:
          type: string
          format: uuid
        unit_id:
          type: string
          format: uuid
        vault:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
            logo_url:
              type:
                - string
                - 'null'
            key_mode:
              type: string
              enum:
                - managed
                - external
              description: >-
                Who signs the vault's receipts. `managed`: Endstate holds the
                vault's key and signs each receipt on the vault's behalf when
                the vault confirms it has an item, so the receipt is not
                independent of Endstate. `external`: the vault holds its own key
                and signs its receipts itself.
              example: managed
          required:
            - id
            - name
            - logo_url
            - key_mode
        status:
          type: string
          enum:
            - pending
            - submitted
            - active
            - unvault_waiting
            - unvaulting
            - unvaulted
            - cancelled
            - expired
            - rejected
            - failed
        notes:
          type:
            - string
            - 'null'
        rejection_reason:
          type:
            - string
            - 'null'
          enum:
            - wrong_item
            - chip_unreadable
            - unacceptable
            - owner_changed
            - null
          description: >-
            Why the request was rejected: the vault's reason, or `owner_changed`
            when the unit changed hands after its owner approved.
        decided_at:
          type:
            - string
            - 'null'
          format: date-time
        available_at:
          type:
            - string
            - 'null'
          format: date-time
        hold:
          type:
            - object
            - 'null'
          properties:
            by:
              type: string
              enum:
                - vault
                - brand
            reason:
              type:
                - string
                - 'null'
          required:
            - by
            - reason
        ready_to_ship:
          type: boolean
          description: >-
            Whether the item is ready to ship: its waiting period has ended and
            nothing holds it. The vault records dispatch with a tap of the item
            from then on.
        dispatched_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            When the vault's tap recorded the item's dispatch. Endstate then
            completes the unvault; the vault hands the item over once the
            request reads `unvaulted`.
        expires_at:
          type:
            - string
            - 'null'
          format: date-time
          description: When a pending request expires; null once it has moved on.
        created_at:
          type: string
          format: date-time
      required:
        - id
        - unit_id
        - vault
        - status
        - notes
        - rejection_reason
        - decided_at
        - available_at
        - hold
        - ready_to_ship
        - dispatched_at
        - expires_at
        - created_at
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              anyOf:
                - $ref: '#/components/schemas/ErrorCode'
                - type: string
              description: >-
                Stable, namespaced error code. The `ErrorCode` catalogue lists
                every code defined today; handle an unrecognized value as a
                generic failure rather than throwing, since codes are added over
                time.
              example: chip.not_found
            message:
              type: string
              description: >-
                Human-readable description, for logs and debugging. Wording may
                change without notice - never parse or match against it.
              example: Chip not found
            request_id:
              type: string
              description: >-
                Identifier for this request, matching the `X-Request-Id`
                response header. Log it and include it in any support request.
              example: req_8e1a7f50-90ab-4cde-f012-3456789abcde
            doc_url:
              type: string
              description: Documentation page for this error code.
              example: https://docs.endstate.io/errors/chip-not-found
            details:
              $ref: '#/components/schemas/ValidationErrorDetails'
          required:
            - code
            - message
            - request_id
            - doc_url
      required:
        - error
      description: >-
        Every error response, regardless of endpoint or HTTP status, uses this
        envelope.
    ErrorCode:
      type: string
      enum:
        - environment.conflict
        - environment.invalid_configuration
        - organization.changed
        - validation.failed
        - auth.unauthorized
        - auth.forbidden
        - session_token.invalid_or_expired
        - session_token.wrong_chip
        - wallet_session.invalid_or_expired
        - not_found.resource
        - rate_limit.exceeded
        - chip.not_found
        - chip.invalid_e_value
        - chip.already_scanned
        - quota.exceeded
        - chip.not_a_test_chip
        - unit.not_found
        - unit.already_exists
        - unit.not_minted
        - collection.not_found
        - collection.already_exists
        - collection.not_active
        - collection_backfill.blocked
        - organization.not_found
        - chip.already_paired
        - chip.key_unusable
        - chip.mac_required
        - chip.bulk_mixed_collections
        - chip.bulk_pending
        - unit.issuance_pending
        - claim.owner_unknown
        - claim.already_to_recipient
        - claim.in_progress
        - claim.locked
        - claim.unit_vaulted
        - claim.unit_held
        - claim.tap_required
        - claim.not_found
        - chip_replacement.locked
        - chip_replacement.in_progress
        - chip_replacement.not_found
        - transfer.owner_unknown
        - transfer.already_to_recipient
        - transfer.in_progress
        - transfer.not_found
        - submission.not_executable
        - vault.not_found
        - vault.already_exists
        - vault.provisioning_failed
        - vault.not_permitted
        - vault.unavailable
        - vault.challenge_invalid
        - vault.address_in_use
        - vault.key_conflict
        - vault.signer_unauthorized
        - vault_connection.not_found
        - vault_connection.not_active
        - vault_connection.already_exists
        - vault_connection.conflict
        - vault.in_progress
        - vault.owner_changed
        - vault.request_not_found
        - vault.request_not_pending
        - vault.consent_invalid
        - vault.consent_used
        - vault.consent_outdated
        - vault.request_rejected
        - vault.request_expired
        - vault.reference_taken
        - idempotency.key_conflict
        - idempotency.in_progress
        - internal.error
        - webhook_endpoint.not_found
        - webhook_endpoint.deleted
        - webhook_endpoint.limit_reached
        - webhook_endpoint.url_refused
        - webhook_endpoint.signing_unavailable
        - webhook_event_type.unknown
        - webhook_event.not_found
        - webhook_endpoint.disabled
        - webhook_delivery.in_progress
        - webhook_endpoint.not_subscribed
      description: >-
        Stable, namespaced error code in `<resource>.<reason>` form. Branch on
        this rather than on `message` or the HTTP status. This is the catalogue
        as of this spec revision, not a closed set - see
        `ErrorResponse.error.code`.
      example: chip.not_found
    ValidationErrorDetails:
      type: object
      properties:
        formErrors:
          type: array
          items:
            type: string
          description: Issues that apply to the request as a whole rather than one field.
          example: []
        fieldErrors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
          description: Validation issues keyed by the field that failed.
          example:
            name:
              - 'Invalid input: expected string, received undefined'
      required:
        - formErrors
        - fieldErrors
      description: Per-field validation detail. Present only on `validation.failed`.
  securitySchemes:
    SessionTokenBearer:
      type: http
      scheme: bearer
      bearerFormat: end_sess
      description: >-
        Use `Authorization: Bearer end_sess_*` for V2 browser session tokens
        (e.g. `end_sess_ZW5kc3RhdGUtZXhhbXBsZS1zZXNzaW9uLXRva2VuAAA`).

````

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