{
  "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"
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "end_sk",
        "description": "Use `Authorization: Bearer end_sk_*` for partner API keys (e.g. `end_sk_AbCd_example_api_key`)."
      },
      "SessionTokenBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "end_sess",
        "description": "Use `Authorization: Bearer end_sess_*` for V2 browser session tokens (e.g. `end_sess_ZW5kc3RhdGUtZXhhbXBsZS1zZXNzaW9uLXRva2VuAAA`)."
      },
      "PublishableKeyBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "end_pk",
        "description": "Use `Authorization: Bearer end_pk_live_*` for V2 publishable keys. Safe to use from a browser: a publishable key identifies your organization and grants no access by itself."
      },
      "WalletIdentityBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Use `Authorization: Bearer <identity token>` - a single-use, short-lived token identifying the customer, issued by your own sign-in."
      }
    },
    "schemas": {
      "ErrorCode": {
        "type": "string",
        "enum": [
          "validation.failed",
          "auth.unauthorized",
          "auth.forbidden",
          "session_token.invalid_or_expired",
          "session_token.wrong_chip",
          "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",
          "chip.already_paired",
          "chip.bulk_mixed_collections",
          "chip.bulk_pending",
          "unit.issuance_pending",
          "claim.owner_unknown",
          "claim.already_to_recipient",
          "claim.in_progress",
          "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",
          "idempotency.key_conflict",
          "idempotency.in_progress",
          "internal.error"
        ],
        "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": ["Required"]
            }
          }
        },
        "required": ["formErrors", "fieldErrors"],
        "description": "Per-field validation detail. Present only on `validation.failed`."
      },
      "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."
      },
      "HealthResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": ["ok"]
          },
          "service": {
            "type": "string",
            "enum": ["endstate-api"]
          },
          "api_base_url": {
            "type": "string",
            "format": "uri"
          },
          "request_id": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "status",
          "service",
          "api_base_url",
          "request_id",
          "timestamp"
        ],
        "example": {
          "status": "ok",
          "service": "endstate-api",
          "api_base_url": "https://api2.endstate.io",
          "request_id": "req_12345678-90ab-4cde-f012-3456789abcde",
          "timestamp": "2026-05-14T10:30:00.000Z"
        }
      },
      "WelcomeResponse": {
        "type": "object",
        "properties": {
          "service": {
            "type": "string",
            "enum": ["endstate-api"]
          },
          "docs": {
            "type": "string",
            "format": "uri"
          },
          "api_versions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "health": {
            "type": "string"
          }
        },
        "required": ["service", "docs", "api_versions", "health"],
        "example": {
          "service": "endstate-api",
          "docs": "https://docs.endstate.io",
          "api_versions": ["v1"],
          "health": "/health"
        }
      },
      "UnitToken": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": ["pending", "active"],
            "description": "Issuance state of this unit within the collection. `pending` until the unit is issued; `active` once issued. There is no failure status: a unit whose issuance cannot complete stays `pending` - if a unit remains `pending` well beyond a few minutes, stop polling and contact support.",
            "example": "pending"
          },
          "serial": {
            "type": ["integer", "null"],
            "description": "Collection-scoped serial number. Assigned when the unit becomes `active`; null until then.",
            "example": 5
          }
        },
        "required": ["status", "serial"]
      },
      "CollectionMembership": {
        "type": ["object", "null"],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": ["string", "null"]
          },
          "external_id": {
            "type": ["string", "null"]
          },
          "contract": {
            "type": "object",
            "properties": {
              "address": {
                "type": "string",
                "description": "Unique address identifying the collection.",
                "example": "0x1111111111111111111111111111111111111111"
              },
              "chain_id": {
                "type": "integer",
                "example": 84532
              },
              "status": {
                "type": "string",
                "enum": ["deploying", "active", "failed"],
                "description": "Collection lifecycle status. `deploying` while the collection is being provisioned; `active` once it is ready to use.",
                "example": "active"
              }
            },
            "required": ["address", "chain_id", "status"]
          },
          "token": {
            "$ref": "#/components/schemas/UnitToken"
          }
        },
        "required": ["id", "name", "external_id", "contract", "token"],
        "description": "The unit's collection. Null only for units created before collections existed."
      },
      "Chip": {
        "type": "object",
        "properties": {
          "chip_id": {
            "type": "string",
            "pattern": "^[0-9A-F]{10}$",
            "description": "Chip identifier from the tap URL.",
            "example": "ABCDEF0123"
          },
          "is_test": {
            "type": "boolean",
            "description": "True for a test chip, false for a chip encoded onto physical hardware."
          },
          "scan_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Number of taps recorded for this chip."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "unit": {
            "type": ["object", "null"],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "external_id": {
                "type": ["string", "null"]
              },
              "name": {
                "type": ["string", "null"]
              },
              "attributes": {
                "type": ["object", "null"],
                "additionalProperties": {}
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "redirect_url": {
                "type": ["string", "null"],
                "format": "uri",
                "description": "Tap redirect URL for this unit; takes precedence over product/collection/org defaults.",
                "example": "https://brand.example/p"
              },
              "collection": {
                "$ref": "#/components/schemas/CollectionMembership"
              }
            },
            "required": [
              "id",
              "external_id",
              "name",
              "attributes",
              "created_at",
              "redirect_url",
              "collection"
            ],
            "description": "Snapshot of the unit this chip is paired to, including its collection and issuance status. Null when the chip is not paired to a unit."
          }
        },
        "required": ["chip_id", "is_test", "scan_count", "created_at", "unit"],
        "example": {
          "chip_id": "ABCDEF0123",
          "is_test": false,
          "scan_count": 47,
          "created_at": "2026-05-16T12:00:00.000Z",
          "unit": {
            "id": "22222222-2222-2222-2222-222222222222",
            "external_id": "unit-001",
            "name": "Example Unit",
            "attributes": {
              "custom_attribute": "value"
            },
            "created_at": "2026-05-16T12:00:00.000Z",
            "redirect_url": null,
            "collection": {
              "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
              "name": "Example Collection",
              "external_id": "collection-001",
              "contract": {
                "address": "0x1111111111111111111111111111111111111111",
                "chain_id": 84532,
                "status": "active"
              },
              "token": {
                "status": "active",
                "serial": 5
              }
            }
          }
        }
      },
      "ChipUnitSnapshot": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "external_id": {
            "type": ["string", "null"]
          },
          "name": {
            "type": ["string", "null"]
          },
          "attributes": {
            "type": ["object", "null"],
            "additionalProperties": {}
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "redirect_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Tap redirect URL for this unit; takes precedence over product/collection/org defaults.",
            "example": "https://brand.example/p"
          },
          "collection": {
            "$ref": "#/components/schemas/CollectionMembership"
          }
        },
        "required": [
          "id",
          "external_id",
          "name",
          "attributes",
          "created_at",
          "redirect_url",
          "collection"
        ],
        "description": "Snapshot of the paired unit, including its collection and issuance status. Poll the unit (GET /v1/units/{unit_id}) until `collection.token.status` is `active`. Issuance has no failure status on this surface - if the unit stays `pending` well beyond a few minutes, stop polling and contact support."
      },
      "CreateChipResponse": {
        "type": "object",
        "properties": {
          "chip_id": {
            "type": "string",
            "pattern": "^[0-9A-F]{10}$",
            "description": "Chip identifier from the tap URL.",
            "example": "ABCDEF0123"
          },
          "is_test": {
            "type": "boolean"
          },
          "unit": {
            "$ref": "#/components/schemas/ChipUnitSnapshot"
          },
          "scan_count": {
            "type": "integer",
            "minimum": 0
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": ["chip_id", "is_test", "unit", "scan_count", "created_at"]
      },
      "CreateChipsBulkResponse": {
        "type": "object",
        "properties": {
          "chips": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateChipResponse"
            }
          }
        },
        "required": ["chips"],
        "description": "The paired chips, in the order provided. Each carries a snapshot of its unit and issuance status."
      },
      "CreateChipsBulkRequest": {
        "type": "object",
        "properties": {
          "chips": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "unit_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Unit (resource) the chip is paired to.",
                      "example": "11111111-1111-1111-1111-111111111111"
                    },
                    "is_test": {
                      "type": "boolean",
                      "enum": [true]
                    }
                  },
                  "required": ["unit_id", "is_test"],
                  "additionalProperties": false
                },
                {
                  "type": "object",
                  "properties": {
                    "unit_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Unit (resource) the chip is paired to.",
                      "example": "11111111-1111-1111-1111-111111111111"
                    },
                    "chip_id": {
                      "type": "string",
                      "pattern": "^[0-9A-Fa-f]{10}$",
                      "description": "Chip identifier from the tap URL. Always returned uppercase.",
                      "example": "ABCDEF0123"
                    },
                    "e": {
                      "type": "string",
                      "pattern": "^[0-9A-F]{32}$",
                      "description": "PICC e-value from the tap URL.",
                      "example": "C78566198547116F3A715DC1C62AF96F"
                    },
                    "c": {
                      "type": "string",
                      "pattern": "^[0-9A-F]{16}$",
                      "description": "Tap verification code (`c`) from the tap URL. Include it whenever the tag provides one.",
                      "example": "940E8AA6628759B3"
                    },
                    "is_test": {
                      "type": "boolean",
                      "enum": [false]
                    }
                  },
                  "required": ["unit_id", "chip_id", "e"],
                  "additionalProperties": false
                }
              ]
            },
            "minItems": 1,
            "maxItems": 100
          }
        },
        "required": ["chips"],
        "description": "Pair up to 100 chips in one request. All units must belong to the same collection; units are issued in the order provided."
      },
      "ChipUnitRef": {
        "type": ["object", "null"],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "external_id": {
            "type": ["string", "null"]
          }
        },
        "required": ["id", "external_id"],
        "description": "The unit this chip is paired to. Null when the chip is not paired to a unit."
      },
      "ChipListItem": {
        "type": "object",
        "properties": {
          "chip_id": {
            "type": "string",
            "pattern": "^[0-9A-F]{10}$",
            "description": "Chip identifier from the tap URL.",
            "example": "ABCDEF0123"
          },
          "is_test": {
            "type": "boolean"
          },
          "scan_count": {
            "type": "integer",
            "minimum": 0
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "unit": {
            "$ref": "#/components/schemas/ChipUnitRef"
          }
        },
        "required": ["chip_id", "is_test", "scan_count", "created_at", "unit"]
      },
      "CursorPagination": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Maximum number of records returned.",
            "example": 50
          },
          "has_more": {
            "type": "boolean",
            "description": "Whether another page is available. Pass `next_cursor` to fetch it.",
            "example": false
          },
          "next_cursor": {
            "type": ["string", "null"],
            "description": "Opaque cursor for the next page. Null when there is no next page.",
            "example": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wNS0xNlQxMjowMDowMC4wMDBaIiwiaWQiOiIzMzMzMzMzMy0zMzMzLTMzMzMtMzMzMy0zMzMzMzMzMzMzMzMifQ"
          }
        },
        "required": ["limit", "has_more", "next_cursor"]
      },
      "ListChipsResponse": {
        "type": "object",
        "properties": {
          "chips": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChipListItem"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/CursorPagination"
          }
        },
        "required": ["chips", "pagination"],
        "example": {
          "chips": [
            {
              "chip_id": "ABCDEF0123",
              "is_test": false,
              "scan_count": 0,
              "created_at": "2026-05-16T12:00:00.000Z",
              "unit": {
                "id": "22222222-2222-2222-2222-222222222222",
                "external_id": "unit-001"
              }
            }
          ],
          "pagination": {
            "limit": 50,
            "has_more": false,
            "next_cursor": null
          }
        }
      },
      "CreateChipRequest": {
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "unit_id": {
                "type": "string",
                "format": "uuid",
                "description": "Unit (resource) the chip is paired to.",
                "example": "11111111-1111-1111-1111-111111111111"
              },
              "is_test": {
                "type": "boolean",
                "enum": [true]
              }
            },
            "required": ["unit_id", "is_test"],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "unit_id": {
                "type": "string",
                "format": "uuid",
                "description": "Unit (resource) the chip is paired to.",
                "example": "11111111-1111-1111-1111-111111111111"
              },
              "chip_id": {
                "type": "string",
                "pattern": "^[0-9A-Fa-f]{10}$",
                "description": "Chip identifier from the tap URL. Always returned uppercase.",
                "example": "ABCDEF0123"
              },
              "e": {
                "type": "string",
                "pattern": "^[0-9A-F]{32}$",
                "description": "PICC e-value from the tap URL.",
                "example": "C78566198547116F3A715DC1C62AF96F"
              },
              "c": {
                "type": "string",
                "pattern": "^[0-9A-F]{16}$",
                "description": "Tap verification code (`c`) from the tap URL. Include it whenever the tag provides one.",
                "example": "940E8AA6628759B3"
              },
              "is_test": {
                "type": "boolean",
                "enum": [false]
              }
            },
            "required": ["unit_id", "chip_id", "e"],
            "additionalProperties": false
          }
        ],
        "description": "Request to pair a chip to a unit. Pass `is_test: true` for a test chip, or `chip_id` + `e` (from the tap URL) for an encoded chip. Always requires `unit_id`."
      },
      "Collection": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "contract": {
            "type": "object",
            "properties": {
              "address": {
                "type": "string",
                "description": "Unique address identifying the collection.",
                "example": "0x1111111111111111111111111111111111111111"
              },
              "chain_id": {
                "type": "integer",
                "example": 84532
              },
              "status": {
                "type": "string",
                "enum": ["deploying", "active", "failed"],
                "description": "Collection lifecycle status. `deploying` while the collection is being provisioned; `active` once it is ready to use.",
                "example": "deploying"
              }
            },
            "required": ["address", "chain_id", "status"]
          },
          "name": {
            "type": ["string", "null"]
          },
          "external_id": {
            "type": ["string", "null"]
          },
          "redirect_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "URL a tap on this collection's units redirects to. Unit- and product-level redirects take precedence over this value.",
            "example": "https://brand.example/p"
          }
        },
        "required": ["id", "contract", "name", "external_id", "redirect_url"]
      },
      "UpdateCollectionRequest": {
        "type": "object",
        "properties": {
          "redirect_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Tap redirect URL for this collection's units. Send null to clear.",
            "example": "https://brand.example/p"
          }
        },
        "required": ["redirect_url"],
        "additionalProperties": false
      },
      "CollectionsListResponse": {
        "type": "object",
        "properties": {
          "collections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Collection"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/CursorPagination"
          }
        },
        "required": ["collections", "pagination"],
        "example": {
          "collections": [
            {
              "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
              "contract": {
                "address": "0x1111111111111111111111111111111111111111",
                "chain_id": 84532,
                "status": "active"
              },
              "name": "Example Collection",
              "external_id": "collection-001",
              "redirect_url": null
            }
          ],
          "pagination": {
            "limit": 50,
            "has_more": false,
            "next_cursor": null
          }
        }
      },
      "CreateCollectionRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Human-readable collection name.",
            "example": "Example Collection"
          },
          "symbol": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4,
            "description": "Short collection symbol (max 4 chars). Derived from `name` when omitted.",
            "example": "EXMP"
          },
          "external_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Your own mapping to this collection (SKU / concept id).",
            "example": "collection-001"
          },
          "chain_id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Identifier of the network to create the collection on. Defaults to your organization's configured network; send a value only to override it where enabled for your organization.",
            "example": 84532
          },
          "redirect_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Tap redirect URL for this collection's units. Omit or null for none; unit- and product-level redirects still take precedence.",
            "example": "https://brand.example/p"
          }
        },
        "required": ["name"],
        "additionalProperties": false
      },
      "PublishableKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The publishable key's identifier."
          },
          "key": {
            "type": "string",
            "description": "The publishable key. Safe to embed in browser code: it identifies your organization and grants no access by itself.",
            "example": "end_pk_live_0123456789abcdef0123456789abcdef"
          },
          "mode": {
            "type": "string",
            "enum": ["live", "sandbox"],
            "description": "The key's mode. Only `live` keys are issued today.",
            "example": "live"
          },
          "name": {
            "type": "string",
            "description": "Display name for the key.",
            "example": "Default"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the key was created."
          }
        },
        "required": ["id", "key", "mode", "name", "created_at"]
      },
      "ListPublishableKeysResponse": {
        "type": "object",
        "properties": {
          "publishable_keys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublishableKey"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/CursorPagination"
          }
        },
        "required": ["publishable_keys", "pagination"]
      },
      "SessionTokenCurrentResponse": {
        "type": "object",
        "properties": {
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO-8601 UTC timestamp after which the token is no longer valid.",
            "example": "2026-05-12T12:44:56.000Z"
          },
          "scope": {
            "type": "object",
            "properties": {
              "chip_id": {
                "type": "string",
                "pattern": "^[0-9A-Fa-f]{10}$",
                "description": "Chip identifier for the tapped chip.",
                "example": "ABCDEF0123"
              },
              "unit_id": {
                "type": "string",
                "format": "uuid",
                "description": "Unit identifier - the resource this token authorizes action on (claim, transfer, etc.).",
                "example": "7f8a3c10-1111-4111-8111-111111111111"
              },
              "organization_id": {
                "type": "string",
                "format": "uuid",
                "example": "99887766-aabb-4ccd-8eef-001122334455"
              }
            },
            "required": ["chip_id", "unit_id", "organization_id"]
          }
        },
        "required": ["expires_at", "scope"],
        "example": {
          "expires_at": "2026-05-12T12:44:56.000Z",
          "scope": {
            "chip_id": "ABCDEF0123",
            "unit_id": "7f8a3c10-1111-4111-8111-111111111111",
            "organization_id": "99887766-aabb-4ccd-8eef-001122334455"
          }
        }
      },
      "CorsOriginsResponse": {
        "type": "object",
        "properties": {
          "cors_origins": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Origins allowed to call the API from a browser, in canonical form.",
            "example": ["https://brand.example", "https://*.brand.example"]
          }
        },
        "required": ["cors_origins"]
      },
      "UpdateCorsOriginsRequest": {
        "type": "object",
        "properties": {
          "cors_origins": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "An exact origin (`https://brand.example`), a subdomain wildcard (`https://*.brand.example`, matching any subdomain depth), or a port wildcard (`http://localhost:*`, matching any port on that host). One wildcard per entry. Port wildcards are intended for local development; remove them before going live.",
              "example": "https://brand.example"
            },
            "maxItems": 50,
            "description": "The complete allow-list. Replaces the previous list; send an empty array to remove all origins."
          }
        },
        "required": ["cors_origins"],
        "additionalProperties": false
      },
      "SettingsResponse": {
        "type": "object",
        "properties": {
          "default_redirect_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Org-wide default tap redirect URL, used only when no unit, product, or collection redirect applies.",
            "example": "https://brand.example"
          },
          "default_chain_id": {
            "type": ["integer", "null"],
            "description": "Identifier of your organization's default network - the network collections are created on when `chain_id` is omitted. `null` when no default network is configured for your organization. Read-only; configured by Endstate.",
            "example": 84532
          },
          "allowed_chain_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "Network identifiers enabled for your organization. An explicit `chain_id` on collection create must be one of these values. Read-only; configured by Endstate.",
            "example": [84532]
          }
        },
        "required": [
          "default_redirect_url",
          "default_chain_id",
          "allowed_chain_ids"
        ]
      },
      "UpdateSettingsRequest": {
        "type": "object",
        "properties": {
          "default_redirect_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Org-wide default tap redirect URL, used only when no unit, product, or collection redirect applies. Send null to clear.",
            "example": "https://brand.example"
          }
        },
        "required": ["default_redirect_url"],
        "additionalProperties": false
      },
      "TapSessionToken": {
        "type": ["object", "null"],
        "properties": {
          "token": {
            "type": "string",
            "example": "end_sess_ZW5kc3RhdGUtZXhhbXBsZS1zZXNzaW9uLXRva2VuAAA"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-05-12T12:44:56.000Z"
          },
          "scope": {
            "type": "object",
            "properties": {
              "chip_id": {
                "type": "string",
                "pattern": "^[0-9A-F]{10}$",
                "example": "ABCDEF0123"
              },
              "unit_id": {
                "type": "string",
                "format": "uuid",
                "example": "7f8a3c10-1111-4111-8111-111111111111"
              },
              "organization_id": {
                "type": "string",
                "format": "uuid",
                "example": "99887766-aabb-4ccd-8eef-001122334455"
              }
            },
            "required": ["chip_id", "unit_id", "organization_id"]
          }
        },
        "required": ["token", "expires_at", "scope"],
        "description": "Opaque session token scoped to this organization, chip, unit, and tap. null on dry_run."
      },
      "TapResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": ["string", "null"],
            "format": "uuid",
            "description": "Tap identifier. null when dry_run is true, because no tap is recorded.",
            "example": "12345678-1234-4567-8901-234567890abc"
          },
          "session_token": {
            "$ref": "#/components/schemas/TapSessionToken"
          },
          "chip": {
            "type": "object",
            "properties": {
              "chip_id": {
                "type": "string",
                "pattern": "^[0-9A-F]{10}$",
                "example": "ABCDEF0123"
              },
              "scan_count": {
                "type": "integer",
                "minimum": 0
              }
            },
            "required": ["chip_id", "scan_count"]
          },
          "unit": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "external_id": {
                "type": ["string", "null"]
              },
              "name": {
                "type": ["string", "null"]
              },
              "attributes": {
                "type": ["object", "null"],
                "additionalProperties": {}
              },
              "collection": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CollectionMembership"
                  },
                  {
                    "description": "The unit's collection and issuance status. Null only for units created before collections existed."
                  }
                ]
              }
            },
            "required": [
              "id",
              "external_id",
              "name",
              "attributes",
              "collection"
            ]
          },
          "redirect_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Canonical verify URL on the org's verified domain. null when no verified domain exists.",
            "example": "https://brand.example/verify/ABCDEF0123"
          },
          "dry_run": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "session_token",
          "chip",
          "unit",
          "redirect_url",
          "dry_run"
        ],
        "example": {
          "id": "12345678-1234-4567-8901-234567890abc",
          "session_token": {
            "token": "end_sess_ZW5kc3RhdGUtZXhhbXBsZS1zZXNzaW9uLXRva2VuAAA",
            "expires_at": "2026-05-12T12:44:56.000Z",
            "scope": {
              "chip_id": "ABCDEF0123",
              "unit_id": "7f8a3c10-1111-4111-8111-111111111111",
              "organization_id": "99887766-aabb-4ccd-8eef-001122334455"
            }
          },
          "chip": {
            "chip_id": "ABCDEF0123",
            "scan_count": 47
          },
          "unit": {
            "id": "7f8a3c10-1111-4111-8111-111111111111",
            "external_id": "unit-001",
            "name": "Example Unit",
            "attributes": {
              "custom_attribute": "value"
            },
            "collection": {
              "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
              "name": "Example Collection",
              "external_id": "collection-001",
              "contract": {
                "address": "0x1111111111111111111111111111111111111111",
                "chain_id": 84532,
                "status": "active"
              },
              "token": {
                "status": "active",
                "serial": 5
              }
            }
          },
          "redirect_url": "https://brand.example/verify/ABCDEF0123",
          "dry_run": false
        }
      },
      "CreateTapRequest": {
        "type": "object",
        "properties": {
          "chip_id": {
            "type": "string",
            "pattern": "^[0-9A-Fa-f]{10}$",
            "description": "Chip identifier from the tap URL.",
            "example": "ABCDEF0123"
          },
          "e": {
            "type": "string",
            "pattern": "^[0-9A-Fa-f]{32}$",
            "description": "The single-use value from the tap URL. Each one works exactly once.",
            "example": "C78566198547116F3A715DC1C62AF96F"
          },
          "c": {
            "type": "string",
            "pattern": "^[0-9A-F]{16}$",
            "description": "Tap verification code (`c`) from the tap URL. Include it whenever the tag provides one.",
            "example": "940E8AA6628759B3"
          },
          "ttl": {
            "type": ["integer", "null"],
            "minimum": 60,
            "maximum": 3600,
            "description": "Session-token TTL override in seconds, between 60 and 3600 inclusive. Secret key only: a publishable-key caller does not choose how long its own credential lives.",
            "example": 600
          },
          "dry_run": {
            "type": "boolean",
            "description": "Validate the tap without recording it or issuing a session token. Requires an admin secret key.",
            "example": false
          }
        },
        "required": ["chip_id", "e"],
        "additionalProperties": false,
        "example": {
          "chip_id": "ABCDEF0123",
          "e": "C78566198547116F3A715DC1C62AF96F"
        }
      },
      "TapListItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The tap's identifier - the same `id` returned by the call that recorded it."
          },
          "chip_id": {
            "type": "string",
            "pattern": "^[0-9A-F]{10}$",
            "description": "Chip identifier from the tap URL.",
            "example": "ABCDEF0123"
          },
          "unit_id": {
            "type": ["string", "null"],
            "format": "uuid",
            "description": "The unit the tapped chip is paired to. Null when the chip is not paired to a unit."
          },
          "count": {
            "type": "integer",
            "minimum": 0,
            "description": "The chip's tap counter value recorded by this tap."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the tap was verified."
          }
        },
        "required": ["id", "chip_id", "unit_id", "count", "created_at"]
      },
      "ListTapsResponse": {
        "type": "object",
        "properties": {
          "taps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TapListItem"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/CursorPagination"
          }
        },
        "required": ["taps", "pagination"]
      },
      "CreateTestTapResponse": {
        "type": "object",
        "properties": {
          "chip_id": {
            "type": "string",
            "example": "ABCDEF0123"
          },
          "e": {
            "type": "string",
            "example": "C78566198547116F3A715DC1C62AF96F"
          },
          "count": {
            "type": "integer",
            "minimum": 0,
            "example": 5
          }
        },
        "required": ["chip_id", "e", "count"]
      },
      "CreateTestTapRequest": {
        "type": "object",
        "properties": {
          "chip_id": {
            "type": "string",
            "pattern": "^[0-9A-Fa-f]{10}$",
            "description": "Identifier of the test chip to generate a tap for.",
            "example": "ABCDEF0123"
          },
          "count": {
            "type": "integer",
            "minimum": 0,
            "description": "Optional counter value. Defaults to the next emulated chip counter and reserves it, so each tap returns a new e-value. Explicit counts must be greater than the chip's current highestObservedCount.",
            "example": 5
          }
        },
        "required": ["chip_id"]
      },
      "ChipReplacement": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "unit_id": {
            "type": "string",
            "format": "uuid"
          },
          "old_chip_id": {
            "type": "string",
            "description": "The chip being retired from the unit."
          },
          "new_chip_id": {
            "type": "string",
            "description": "The chip replacing it."
          },
          "status": {
            "type": "string",
            "enum": ["prepared", "submitted", "confirmed", "failed"],
            "description": "Replacement progress. Poll until `confirmed`; `failed` is terminal."
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "unit_id",
          "old_chip_id",
          "new_chip_id",
          "status",
          "created_at"
        ],
        "example": {
          "id": "00000000-0000-4000-8000-000000000000",
          "unit_id": "22222222-2222-4222-8222-222222222222",
          "old_chip_id": "ABCDEF0122",
          "new_chip_id": "ABCDEF0123",
          "status": "submitted",
          "created_at": "2026-06-15T12:00:00.000Z"
        }
      },
      "CreateChipReplacementRequest": {
        "type": "object",
        "properties": {
          "new_chip_id": {
            "type": "string",
            "minLength": 1,
            "description": "The chip that replaces the unit's current chip. Must already exist and not be paired to any unit.",
            "example": "ABCDEF0123"
          }
        },
        "required": ["new_chip_id"],
        "example": {
          "new_chip_id": "ABCDEF0123"
        }
      },
      "Claim": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "unit_id": {
            "type": "string",
            "format": "uuid"
          },
          "to": {
            "type": "string",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "from": {
            "type": "string",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "status": {
            "type": "string",
            "enum": ["claiming", "claimed", "expired", "failed"]
          },
          "submission": {
            "type": ["object", "null"],
            "properties": {
              "to": {
                "type": "string",
                "pattern": "^0x[a-fA-F0-9]{40}$"
              },
              "data": {
                "type": "string"
              }
            },
            "required": ["to", "data"]
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": ["id", "unit_id", "to", "from", "status", "created_at"],
        "example": {
          "id": "00000000-0000-4000-8000-000000000000",
          "unit_id": "22222222-2222-4222-8222-222222222222",
          "to": "0x1111111111111111111111111111111111111111",
          "from": "0x2222222222222222222222222222222222222222",
          "status": "claiming",
          "created_at": "2026-06-15T12:00:00.000Z"
        }
      },
      "CreateClaimRequest": {
        "type": "object",
        "properties": {
          "to": {
            "type": "string",
            "pattern": "^0x[a-fA-F0-9]{40}$",
            "description": "Recipient address.",
            "example": "0x1111111111111111111111111111111111111111"
          },
          "execution": {
            "type": "string",
            "enum": ["endstate_relay", "client_broadcast"],
            "default": "endstate_relay",
            "description": "`endstate_relay` submits the claim for you. `client_broadcast` returns a claim payload for you to submit.",
            "example": "endstate_relay"
          }
        },
        "required": ["to"],
        "example": {
          "to": "0x1111111111111111111111111111111111111111",
          "execution": "endstate_relay"
        }
      },
      "UnitChip": {
        "type": "object",
        "properties": {
          "chip_id": {
            "type": "string",
            "description": "Chip identifier from the tap URL.",
            "example": "ABCDEF0123"
          },
          "is_test": {
            "type": "boolean"
          },
          "scan_count": {
            "type": "integer",
            "minimum": 0
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": ["chip_id", "is_test", "scan_count", "created_at"]
      },
      "Unit": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "external_id": {
            "type": ["string", "null"]
          },
          "name": {
            "type": ["string", "null"]
          },
          "attributes": {
            "type": ["object", "null"],
            "additionalProperties": {}
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "redirect_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Tap redirect URL for this unit; takes precedence over product/collection/org defaults.",
            "example": "https://brand.example/p"
          },
          "collection": {
            "$ref": "#/components/schemas/CollectionMembership"
          },
          "chips": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UnitChip"
            },
            "description": "Chips paired to this unit."
          }
        },
        "required": [
          "id",
          "external_id",
          "name",
          "attributes",
          "created_at",
          "redirect_url",
          "collection",
          "chips"
        ]
      },
      "UpdateUnitRequest": {
        "type": "object",
        "properties": {
          "redirect_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Tap redirect URL for this unit. Send null to clear.",
            "example": "https://brand.example/p"
          }
        },
        "required": ["redirect_url"],
        "additionalProperties": false
      },
      "Transfer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "unit_id": {
            "type": "string",
            "format": "uuid"
          },
          "to": {
            "type": "string",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "from": {
            "type": "string",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "status": {
            "type": "string",
            "enum": ["prepared", "confirmed", "expired", "failed"]
          },
          "transaction": {
            "type": ["object", "null"],
            "properties": {
              "to": {
                "type": "string",
                "pattern": "^0x[a-fA-F0-9]{40}$"
              },
              "data": {
                "type": "string"
              }
            },
            "required": ["to", "data"],
            "description": "Payload the unit's current owner submits to complete the transfer. Returned when the transfer is created."
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": ["id", "unit_id", "to", "from", "status", "created_at"],
        "example": {
          "id": "00000000-0000-4000-8000-000000000000",
          "unit_id": "22222222-2222-4222-8222-222222222222",
          "to": "0x1111111111111111111111111111111111111111",
          "from": "0x2222222222222222222222222222222222222222",
          "status": "prepared",
          "transaction": {
            "to": "0x3333333333333333333333333333333333333333",
            "data": "0xa9059cbb"
          },
          "created_at": "2026-06-15T12:00:00.000Z"
        }
      },
      "CreateTransferRequest": {
        "type": "object",
        "properties": {
          "to": {
            "type": "string",
            "pattern": "^0x[a-fA-F0-9]{40}$",
            "description": "Address that will receive the unit.",
            "example": "0x1111111111111111111111111111111111111111"
          }
        },
        "required": ["to"],
        "example": {
          "to": "0x1111111111111111111111111111111111111111"
        }
      },
      "UnitsListResponse": {
        "type": "object",
        "properties": {
          "units": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Unit"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/CursorPagination"
          }
        },
        "required": ["units", "pagination"],
        "example": {
          "units": [
            {
              "id": "33333333-3333-3333-3333-333333333333",
              "external_id": "unit-001",
              "name": "Example Unit",
              "attributes": {
                "custom_attribute": "value"
              },
              "created_at": "2026-05-16T12:00:00.000Z",
              "redirect_url": null,
              "collection": null,
              "chips": []
            }
          ],
          "pagination": {
            "limit": 50,
            "has_more": false,
            "next_cursor": null
          }
        }
      },
      "CreateUnitRequest": {
        "type": "object",
        "properties": {
          "external_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Unique identifier for the unit within your organization.",
            "example": "unit-001"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "Human-readable name for the unit.",
            "example": "Example Unit"
          },
          "attributes": {
            "type": "object",
            "additionalProperties": {},
            "description": "Free-form JSON attributes for the unit.",
            "example": {
              "custom_attribute": "value"
            }
          },
          "collection_id": {
            "type": "string",
            "format": "uuid",
            "description": "Collection this unit belongs to.",
            "example": "8e1a7f50-90ab-4cde-8012-3456789abcde"
          },
          "redirect_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Tap redirect URL for this unit. Omit or null for none.",
            "example": "https://brand.example/p"
          }
        },
        "required": ["collection_id"],
        "additionalProperties": false
      },
      "CreateWalletSessionResponse": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "Credential the wallet presents to establish the customer's session."
          },
          "session_token": {
            "type": "string",
            "description": "Credential the wallet presents when resolving an operation to sign."
          },
          "expires_at": {
            "type": "integer",
            "description": "Unix seconds at which the session lapses."
          },
          "chain_id": {
            "type": "integer",
            "description": "Network the session operates on."
          }
        },
        "required": ["token", "session_token", "expires_at", "chain_id"],
        "example": {
          "token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiJ9...",
          "session_token": "end_wsess_AbCd_example_token",
          "expires_at": 1755640000,
          "chain_id": 8453
        }
      },
      "CreateWalletSessionRequest": {
        "type": "object",
        "properties": {
          "publishable_key": {
            "type": "string",
            "minLength": 1,
            "description": "The publishable key of the site the wallet is embedded in."
          }
        },
        "required": ["publishable_key"],
        "example": {
          "publishable_key": "end_pk_live_0123456789abcdef0123456789abcdef"
        }
      }
    },
    "parameters": {}
  },
  "paths": {
    "/health": {
      "get": {
        "operationId": "getServiceHealth",
        "summary": "Service health check",
        "description": "Returns the API's operational status. No authentication required. Suitable for load-balancer health probes and uptime monitoring.",
        "tags": ["Service"],
        "responses": {
          "200": {
            "description": "Service is healthy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                },
                "example": {
                  "status": "ok",
                  "service": "endstate-api",
                  "api_base_url": "https://api2.endstate.io",
                  "request_id": "req_12345678-90ab-4cde-f012-3456789abcde",
                  "timestamp": "2026-05-14T10:30:00.000Z"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/": {
      "get": {
        "operationId": "getServiceInfo",
        "summary": "Service welcome",
        "description": "Service welcome message with pointers to docs and available API versions. No authentication.",
        "tags": ["Service"],
        "responses": {
          "200": {
            "description": "Service info",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WelcomeResponse"
                },
                "example": {
                  "service": "endstate-api",
                  "docs": "https://docs.endstate.io",
                  "api_versions": ["v1"],
                  "health": "/health"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/chips/{chip_id}": {
      "get": {
        "operationId": "getChip",
        "summary": "Fetch a chip",
        "description": "Returns a single chip by its identifier, including a snapshot of the unit it is paired to and that unit's issuance status.",
        "tags": ["Chips"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "pattern": "^[0-9A-Fa-f]{10}$",
              "description": "Chip identifier from the tap URL. Always returned uppercase.",
              "example": "ABCDEF0123"
            },
            "required": true,
            "name": "chip_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The chip.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Chip"
                },
                "example": {
                  "chip_id": "ABCDEF0123",
                  "is_test": false,
                  "scan_count": 47,
                  "created_at": "2026-05-16T12:00:00.000Z",
                  "unit": {
                    "id": "22222222-2222-2222-2222-222222222222",
                    "external_id": "unit-001",
                    "name": "Example Unit",
                    "attributes": {
                      "custom_attribute": "value"
                    },
                    "created_at": "2026-05-16T12:00:00.000Z",
                    "redirect_url": null,
                    "collection": {
                      "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                      "name": "Example Collection",
                      "external_id": "collection-001",
                      "contract": {
                        "address": "0x1111111111111111111111111111111111111111",
                        "chain_id": 84532,
                        "status": "active"
                      },
                      "token": {
                        "status": "active",
                        "serial": 5
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `chip.not_found` | The chip ID does not exist in your organization. |",
            "x-error-codes": ["chip.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.not_found": {
                    "summary": "The chip ID does not exist in your organization.",
                    "value": {
                      "error": {
                        "code": "chip.not_found",
                        "message": "The chip ID does not exist in your organization.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-not-found"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/chips/bulk": {
      "post": {
        "operationId": "bulkPairChips",
        "summary": "Pair chips in bulk",
        "description": "Pairs up to 100 chips to units of a single collection in one request. Units are issued in the order provided.",
        "tags": ["Chips"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "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,
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateChipsBulkRequest"
              },
              "example": {
                "chips": [
                  {
                    "unit_id": "22222222-2222-2222-2222-222222222222",
                    "is_test": true
                  },
                  {
                    "unit_id": "33333333-3333-3333-3333-333333333333",
                    "chip_id": "ABCDEF0123",
                    "e": "C78566198547116F3A715DC1C62AF96F"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created chips, in the order provided.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateChipsBulkResponse"
                },
                "example": {
                  "chips": [
                    {
                      "chip_id": "FEDCBA9876",
                      "is_test": true,
                      "unit": {
                        "id": "22222222-2222-2222-2222-222222222222",
                        "external_id": "unit-001",
                        "name": "Example Unit",
                        "attributes": null,
                        "created_at": "2026-05-16T12:00:00.000Z",
                        "collection": {
                          "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                          "name": "Example Collection",
                          "external_id": "collection-001",
                          "contract": {
                            "address": "0x1111111111111111111111111111111111111111",
                            "chain_id": 84532,
                            "status": "active"
                          },
                          "token": {
                            "status": "pending",
                            "serial": null
                          }
                        }
                      },
                      "scan_count": 0,
                      "created_at": "2026-05-16T12:00:00.000Z"
                    },
                    {
                      "chip_id": "ABCDEF0123",
                      "is_test": false,
                      "unit": {
                        "id": "33333333-3333-3333-3333-333333333333",
                        "external_id": "unit-002",
                        "name": "Example Unit",
                        "attributes": null,
                        "created_at": "2026-05-16T12:00:00.000Z",
                        "collection": {
                          "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                          "name": "Example Collection",
                          "external_id": "collection-001",
                          "contract": {
                            "address": "0x1111111111111111111111111111111111111111",
                            "chain_id": 84532,
                            "status": "active"
                          },
                          "token": {
                            "status": "pending",
                            "serial": null
                          }
                        }
                      },
                      "scan_count": 0,
                      "created_at": "2026-05-16T12:00:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `chip.bulk_mixed_collections` | A bulk pairing request included units from more than one collection. Pair one collection per request. |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": [
              "chip.bulk_mixed_collections",
              "validation.failed"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.bulk_mixed_collections": {
                    "summary": "A bulk pairing request included units from more than one collection. Pair one collection per request.",
                    "value": {
                      "error": {
                        "code": "chip.bulk_mixed_collections",
                        "message": "A bulk pairing request included units from more than one collection. Pair one collection per request.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-bulk-mixed-collections"
                      }
                    }
                  },
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted.\n\n| Error code | When |\n| --- | --- |\n| `auth.forbidden` | Credential is valid but does not have access to the requested resource or action. |",
            "x-error-codes": ["auth.forbidden"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `chip.not_found` | The chip ID does not exist in your organization. |\n| `unit.not_found` | The unit ID does not exist in your organization. |",
            "x-error-codes": ["chip.not_found", "unit.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.not_found": {
                    "summary": "The chip ID does not exist in your organization.",
                    "value": {
                      "error": {
                        "code": "chip.not_found",
                        "message": "The chip ID does not exist in your organization.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-not-found"
                      }
                    }
                  },
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The request conflicts with the resource's current state.\n\n| Error code | When |\n| --- | --- |\n| `chip.already_paired` | The chip is already paired to a unit and cannot be paired again. |\n| `chip.bulk_pending` | A bulk pairing for this collection is still processing. Retry once it completes. |\n| `collection.not_active` | The collection has not finished provisioning. Wait until its status is `active`. |\n| `idempotency.in_progress` | A request with this `Idempotency-Key` is already in progress. |\n| `idempotency.key_conflict` | The `Idempotency-Key` was previously used with a different request body. |\n| `unit.issuance_pending` | One or more units already have an issuance in progress. Wait for it to settle. |",
            "x-error-codes": [
              "chip.already_paired",
              "chip.bulk_pending",
              "collection.not_active",
              "idempotency.in_progress",
              "idempotency.key_conflict",
              "unit.issuance_pending"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.already_paired": {
                    "summary": "The chip is already paired to a unit and cannot be paired again.",
                    "value": {
                      "error": {
                        "code": "chip.already_paired",
                        "message": "The chip is already paired to a unit and cannot be paired again.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-already-paired"
                      }
                    }
                  },
                  "chip.bulk_pending": {
                    "summary": "A bulk pairing for this collection is still processing. Retry once it completes.",
                    "value": {
                      "error": {
                        "code": "chip.bulk_pending",
                        "message": "A bulk pairing for this collection is still processing. Retry once it completes.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-bulk-pending"
                      }
                    }
                  },
                  "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.issuance_pending": {
                    "summary": "One or more units already have an issuance in progress. Wait for it to settle.",
                    "value": {
                      "error": {
                        "code": "unit.issuance_pending",
                        "message": "One or more units already have an issuance in progress. Wait for it to settle.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/unit-issuance-pending"
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "The request was well-formed but could not be processed.\n\n| Error code | When |\n| --- | --- |\n| `chip.invalid_e_value` | The tap credential (`e` value) was rejected as invalid. |",
            "x-error-codes": ["chip.invalid_e_value"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.invalid_e_value": {
                    "summary": "The tap credential (`e` value) was rejected as invalid.",
                    "value": {
                      "error": {
                        "code": "chip.invalid_e_value",
                        "message": "The tap credential (`e` value) was rejected as invalid.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-invalid-e-value"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/chips": {
      "get": {
        "operationId": "listChips",
        "summary": "List chips",
        "description": "Returns a cursor-paginated list of chips in your organization, each with a reference to the unit it is paired to. Use `is_test` to filter to test or encoded chips.",
        "tags": ["Chips"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "description": "Maximum number of records to return. Defaults to 50; maximum 100."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Opaque cursor from the previous page."
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": ["true", "false"],
              "description": "Filter by test-chip flag. Omit to return both test and encoded chips."
            },
            "required": false,
            "name": "is_test",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of chips.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListChipsResponse"
                },
                "example": {
                  "chips": [
                    {
                      "chip_id": "ABCDEF0123",
                      "is_test": true,
                      "scan_count": 0,
                      "created_at": "2026-05-16T12:00:00.000Z",
                      "unit": {
                        "id": "22222222-2222-2222-2222-222222222222",
                        "external_id": "unit-001"
                      }
                    }
                  ],
                  "pagination": {
                    "limit": 50,
                    "has_more": false,
                    "next_cursor": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "pairChip",
        "summary": "Pair a chip",
        "description": "Creates a chip in your organization paired to the unit you specify.",
        "tags": ["Chips"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "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,
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateChipRequest"
              },
              "example": {
                "unit_id": "22222222-2222-2222-2222-222222222222",
                "is_test": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created chip.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateChipResponse"
                },
                "example": {
                  "chip_id": "ABCDEF0123",
                  "is_test": true,
                  "unit": {
                    "id": "22222222-2222-2222-2222-222222222222",
                    "external_id": "unit-001",
                    "name": "Example Unit",
                    "attributes": null,
                    "created_at": "2026-05-16T12:00:00.000Z",
                    "collection": {
                      "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                      "name": "Example Collection",
                      "external_id": "collection-001",
                      "contract": {
                        "address": "0x1111111111111111111111111111111111111111",
                        "chain_id": 84532,
                        "status": "active"
                      },
                      "token": {
                        "status": "pending",
                        "serial": null
                      }
                    }
                  },
                  "scan_count": 0,
                  "created_at": "2026-05-16T12:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted.\n\n| Error code | When |\n| --- | --- |\n| `auth.forbidden` | Credential is valid but does not have access to the requested resource or action. |",
            "x-error-codes": ["auth.forbidden"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `chip.not_found` | The chip ID does not exist in your organization. |\n| `unit.not_found` | The unit ID does not exist in your organization. |",
            "x-error-codes": ["chip.not_found", "unit.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.not_found": {
                    "summary": "The chip ID does not exist in your organization.",
                    "value": {
                      "error": {
                        "code": "chip.not_found",
                        "message": "The chip ID does not exist in your organization.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-not-found"
                      }
                    }
                  },
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The request conflicts with the resource's current state.\n\n| Error code | When |\n| --- | --- |\n| `chip.already_paired` | The chip is already paired to a unit and cannot be paired again. |\n| `collection.not_active` | The collection has not finished provisioning. Wait until its status is `active`. |\n| `idempotency.in_progress` | A request with this `Idempotency-Key` is already in progress. |\n| `idempotency.key_conflict` | The `Idempotency-Key` was previously used with a different request body. |\n| `unit.issuance_pending` | One or more units already have an issuance in progress. Wait for it to settle. |",
            "x-error-codes": [
              "chip.already_paired",
              "collection.not_active",
              "idempotency.in_progress",
              "idempotency.key_conflict",
              "unit.issuance_pending"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.already_paired": {
                    "summary": "The chip is already paired to a unit and cannot be paired again.",
                    "value": {
                      "error": {
                        "code": "chip.already_paired",
                        "message": "The chip is already paired to a unit and cannot be paired again.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-already-paired"
                      }
                    }
                  },
                  "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.issuance_pending": {
                    "summary": "One or more units already have an issuance in progress. Wait for it to settle.",
                    "value": {
                      "error": {
                        "code": "unit.issuance_pending",
                        "message": "One or more units already have an issuance in progress. Wait for it to settle.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/unit-issuance-pending"
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "The request was well-formed but could not be processed.\n\n| Error code | When |\n| --- | --- |\n| `chip.invalid_e_value` | The tap credential (`e` value) was rejected as invalid. |",
            "x-error-codes": ["chip.invalid_e_value"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.invalid_e_value": {
                    "summary": "The tap credential (`e` value) was rejected as invalid.",
                    "value": {
                      "error": {
                        "code": "chip.invalid_e_value",
                        "message": "The tap credential (`e` value) was rejected as invalid.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-invalid-e-value"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/collections/{collection_id}": {
      "get": {
        "operationId": "getCollection",
        "summary": "Fetch a collection",
        "description": "Returns a single collection by id.",
        "tags": ["Collections"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Collection id.",
              "example": "8e1a7f50-90ab-4cde-8012-3456789abcde"
            },
            "required": true,
            "name": "collection_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The collection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Collection"
                },
                "example": {
                  "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                  "contract": {
                    "address": "0x1111111111111111111111111111111111111111",
                    "chain_id": 84532,
                    "status": "active"
                  },
                  "name": "Example Collection",
                  "external_id": "collection-001",
                  "redirect_url": null
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `collection.not_found` | The collection ID does not exist in your organization. |",
            "x-error-codes": ["collection.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "collection.not_found": {
                    "summary": "The collection ID does not exist in your organization.",
                    "value": {
                      "error": {
                        "code": "collection.not_found",
                        "message": "The collection ID does not exist in your organization.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/collection-not-found"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "updateCollection",
        "summary": "Update a collection",
        "description": "Updates a collection's tap redirect URL. Send null to clear.",
        "tags": ["Collections"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Collection id.",
              "example": "8e1a7f50-90ab-4cde-8012-3456789abcde"
            },
            "required": true,
            "name": "collection_id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCollectionRequest"
              },
              "example": {
                "redirect_url": "https://brand.example/p"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated collection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Collection"
                },
                "example": {
                  "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                  "contract": {
                    "address": "0x1111111111111111111111111111111111111111",
                    "chain_id": 84532,
                    "status": "active"
                  },
                  "name": "Example Collection",
                  "external_id": "collection-001",
                  "redirect_url": "https://brand.example/p"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `collection.not_found` | The collection ID does not exist in your organization. |",
            "x-error-codes": ["collection.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "collection.not_found": {
                    "summary": "The collection ID does not exist in your organization.",
                    "value": {
                      "error": {
                        "code": "collection.not_found",
                        "message": "The collection ID does not exist in your organization.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/collection-not-found"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/collections": {
      "get": {
        "operationId": "listCollections",
        "summary": "List collections",
        "description": "Returns a cursor-paginated list of collections. Use `external_id` to filter to one caller-supplied identifier.",
        "tags": ["Collections"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "description": "Maximum number of records to return. Defaults to 50; maximum 100."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Opaque cursor from the previous page."
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Filter to collections with this external identifier."
            },
            "required": false,
            "name": "external_id",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of collections.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CollectionsListResponse"
                },
                "example": {
                  "collections": [
                    {
                      "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                      "contract": {
                        "address": "0x1111111111111111111111111111111111111111",
                        "chain_id": 84532,
                        "status": "active"
                      },
                      "name": "Example Collection",
                      "external_id": "collection-001",
                      "redirect_url": null
                    }
                  ],
                  "pagination": {
                    "limit": 50,
                    "has_more": false,
                    "next_cursor": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createCollection",
        "summary": "Create a collection",
        "description": "Creates a collection and returns it immediately in `deploying` status with its unique address. Provisioning completes asynchronously; the status becomes `active` once the collection is ready to use.",
        "tags": ["Collections"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "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,
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCollectionRequest"
              },
              "example": {
                "name": "Example Collection",
                "symbol": "EXMP",
                "external_id": "collection-001",
                "redirect_url": "https://brand.example/p"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created collection. `contract.status` is usually `deploying`, and may be `failed` if provisioning could not be queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Collection"
                },
                "example": {
                  "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                  "contract": {
                    "address": "0x1111111111111111111111111111111111111111",
                    "chain_id": 84532,
                    "status": "deploying"
                  },
                  "name": "Example Collection",
                  "external_id": "collection-001",
                  "redirect_url": "https://brand.example/p"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The request conflicts with the resource's current state.\n\n| Error code | When |\n| --- | --- |\n| `collection.already_exists` | A collection with the supplied `external_id` already exists in your organization. |\n| `idempotency.in_progress` | A request with this `Idempotency-Key` is already in progress. |\n| `idempotency.key_conflict` | The `Idempotency-Key` was previously used with a different request body. |",
            "x-error-codes": [
              "collection.already_exists",
              "idempotency.in_progress",
              "idempotency.key_conflict"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "collection.already_exists": {
                    "summary": "A collection with the supplied `external_id` already exists in your organization.",
                    "value": {
                      "error": {
                        "code": "collection.already_exists",
                        "message": "A collection with the supplied `external_id` already exists in your organization.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/collection-already-exists"
                      }
                    }
                  },
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/publishable-keys": {
      "get": {
        "operationId": "listPublishableKeys",
        "summary": "List publishable keys",
        "description": "Returns your active publishable keys, oldest first. If you have none, one is created automatically, so a first call always returns a usable key. A publishable key is safe to embed in browser code: it identifies your organization and grants no access by itself.",
        "tags": ["Publishable keys"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "description": "Maximum number of records to return. Defaults to 50; maximum 100."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Opaque cursor from the previous page."
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of publishable keys.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListPublishableKeysResponse"
                },
                "example": {
                  "publishable_keys": [
                    {
                      "id": "00000000-0000-4000-8000-000000000001",
                      "key": "end_pk_live_0123456789abcdef0123456789abcdef",
                      "mode": "live",
                      "name": "Default",
                      "created_at": "2026-08-01T12:00:00.000Z"
                    }
                  ],
                  "pagination": {
                    "limit": 50,
                    "has_more": false,
                    "next_cursor": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/session-tokens/current": {
      "get": {
        "operationId": "getCurrentSessionToken",
        "summary": "Introspect the bearer session token",
        "description": "Returns the scope of the session token presented in the `Authorization: Bearer end_sess_*` header.",
        "tags": ["Session Tokens"],
        "security": [
          {
            "SessionTokenBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Session token scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionTokenCurrentResponse"
                },
                "example": {
                  "expires_at": "2026-05-12T12:44:56.000Z",
                  "scope": {
                    "chip_id": "ABCDEF0123",
                    "unit_id": "7f8a3c10-1111-4111-8111-111111111111",
                    "organization_id": "99887766-aabb-4ccd-8eef-001122334455"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted.\n\n| Error code | When |\n| --- | --- |\n| `auth.forbidden` | Credential is valid but does not have access to the requested resource or action. |",
            "x-error-codes": ["auth.forbidden"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "revokeCurrentSessionToken",
        "summary": "Revoke the bearer session token",
        "description": "Immediately invalidates the session token presented in the `Authorization: Bearer end_sess_*` header. Subsequent requests with the token are rejected as expired. Revoke when a client flow finishes early rather than waiting for the token's expiry.",
        "tags": ["Session Tokens"],
        "security": [
          {
            "SessionTokenBearer": []
          }
        ],
        "responses": {
          "204": {
            "description": "The session token has been revoked."
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted.\n\n| Error code | When |\n| --- | --- |\n| `auth.forbidden` | Credential is valid but does not have access to the requested resource or action. |",
            "x-error-codes": ["auth.forbidden"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/settings/cors-origins": {
      "get": {
        "operationId": "listCorsOrigins",
        "summary": "List CORS origins",
        "description": "Returns the origins allowed to call the API from a browser. Requests made with browser credentials from any other origin are rejected.",
        "tags": ["Settings"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "The allowed origins.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CorsOriginsResponse"
                },
                "example": {
                  "cors_origins": [
                    "https://brand.example",
                    "https://*.brand.example"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "replaceCorsOrigins",
        "summary": "Replace CORS origins",
        "description": "Replaces the full list of origins allowed to call the API from a browser. Each entry is an exact origin, a subdomain wildcard like `https://*.brand.example`, or a port wildcard like `http://localhost:*`; entries are stored in canonical form and returned as saved. Send an empty array to remove all origins. Changes take effect within about a minute. Every origin on this list can call the API from a browser, so remove development origins such as `http://localhost:*` before you go live.",
        "tags": ["Settings"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCorsOriginsRequest"
              },
              "example": {
                "cors_origins": [
                  "https://brand.example",
                  "https://*.brand.example"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved allowed origins.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CorsOriginsResponse"
                },
                "example": {
                  "cors_origins": [
                    "https://brand.example",
                    "https://*.brand.example"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/settings": {
      "get": {
        "operationId": "getSettings",
        "summary": "Get org settings",
        "description": "Returns org-level settings: the default tap redirect URL and your organization's network configuration (read-only, configured by Endstate).",
        "tags": ["Settings"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "The org settings.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SettingsResponse"
                },
                "example": {
                  "default_redirect_url": "https://brand.example",
                  "default_chain_id": 84532,
                  "allowed_chain_ids": [84532]
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "updateSettings",
        "summary": "Update org settings",
        "description": "Sets the org-wide default tap redirect URL. Send null to clear. Network settings are read-only and returned for reference.",
        "tags": ["Settings"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSettingsRequest"
              },
              "example": {
                "default_redirect_url": "https://brand.example"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated org settings.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SettingsResponse"
                },
                "example": {
                  "default_redirect_url": "https://brand.example",
                  "default_chain_id": 84532,
                  "allowed_chain_ids": [84532]
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/taps": {
      "post": {
        "operationId": "createTap",
        "summary": "Record a tap and issue a session token",
        "description": "Exchanges a tap for a session token. Send the `chip_id` and the single-use `e` value from the tap URL; the response is the token, when it expires, and what it is bound to, alongside the verified unit. Each `e` works exactly once. Accepts either an API key, when your server makes the call, or a publishable key, when the page makes it directly with no backend of your own behind it.",
        "tags": ["Taps"],
        "security": [
          {
            "ApiKeyBearer": []
          },
          {
            "PublishableKeyBearer": []
          }
        ],
        "parameters": [
          {
            "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,
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTapRequest"
              },
              "example": {
                "chip_id": "ABCDEF0123",
                "e": "C78566198547116F3A715DC1C62AF96F"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The session token, its expiry, and the verified unit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TapResponse"
                },
                "example": {
                  "id": "12345678-1234-4567-8901-234567890abc",
                  "session_token": {
                    "token": "end_sess_ZW5kc3RhdGUtZXhhbXBsZS1zZXNzaW9uLXRva2VuAAA",
                    "expires_at": "2026-05-12T12:44:56.000Z",
                    "scope": {
                      "chip_id": "ABCDEF0123",
                      "unit_id": "7f8a3c10-1111-4111-8111-111111111111",
                      "organization_id": "99887766-aabb-4ccd-8eef-001122334455"
                    }
                  },
                  "chip": {
                    "chip_id": "ABCDEF0123",
                    "scan_count": 47
                  },
                  "unit": {
                    "id": "7f8a3c10-1111-4111-8111-111111111111",
                    "external_id": "unit-001",
                    "name": "Example Unit",
                    "attributes": {
                      "custom_attribute": "value"
                    },
                    "collection": {
                      "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                      "name": "Example Collection",
                      "external_id": "collection-001",
                      "contract": {
                        "address": "0x1111111111111111111111111111111111111111",
                        "chain_id": 84532,
                        "status": "active"
                      },
                      "token": {
                        "status": "active",
                        "serial": 5
                      }
                    }
                  },
                  "redirect_url": "https://brand.example/verify/ABCDEF0123",
                  "dry_run": false
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted.\n\n| Error code | When |\n| --- | --- |\n| `auth.forbidden` | Credential is valid but does not have access to the requested resource or action. |",
            "x-error-codes": ["auth.forbidden"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `chip.not_found` | The chip ID does not exist in your organization. |\n| `unit.not_found` | The unit ID does not exist in your organization. |",
            "x-error-codes": ["chip.not_found", "unit.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.not_found": {
                    "summary": "The chip ID does not exist in your organization.",
                    "value": {
                      "error": {
                        "code": "chip.not_found",
                        "message": "The chip ID does not exist in your organization.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-not-found"
                      }
                    }
                  },
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The request conflicts with the resource's current state.\n\n| Error code | When |\n| --- | --- |\n| `idempotency.in_progress` | A request with this `Idempotency-Key` is already in progress. |\n| `idempotency.key_conflict` | The `Idempotency-Key` was previously used with a different request body. |",
            "x-error-codes": [
              "idempotency.in_progress",
              "idempotency.key_conflict"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "410": {
            "description": "The credential has already been consumed.\n\n| Error code | When |\n| --- | --- |\n| `chip.already_scanned` | The tap credential has already been used. Each tap produces a single-use credential. |",
            "x-error-codes": ["chip.already_scanned"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.already_scanned": {
                    "summary": "The tap credential has already been used. Each tap produces a single-use credential.",
                    "value": {
                      "error": {
                        "code": "chip.already_scanned",
                        "message": "The tap credential has already been used. Each tap produces a single-use credential.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-already-scanned"
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "The request was well-formed but could not be processed.\n\n| Error code | When |\n| --- | --- |\n| `chip.invalid_e_value` | The tap credential (`e` value) was rejected as invalid. |",
            "x-error-codes": ["chip.invalid_e_value"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.invalid_e_value": {
                    "summary": "The tap credential (`e` value) was rejected as invalid.",
                    "value": {
                      "error": {
                        "code": "chip.invalid_e_value",
                        "message": "The tap credential (`e` value) was rejected as invalid.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-invalid-e-value"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listTaps",
        "summary": "List taps",
        "description": "Returns a cursor-paginated history of verified taps, oldest first. Each item's `id` is the same identifier returned by the call that recorded the tap. Filter by chip, unit, or time window; by default only taps of encoded chips on physical items are returned - set `is_test` to `true` for test-chip taps instead.",
        "tags": ["Taps"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "description": "Maximum number of records to return. Defaults to 50; maximum 100."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Opaque cursor from the previous page."
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^[0-9A-F]{10}$",
              "description": "Only taps of this chip.",
              "example": "ABCDEF0123"
            },
            "required": false,
            "name": "chip_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Only taps of chips paired to this unit."
            },
            "required": false,
            "name": "unit_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only taps recorded strictly after this ISO-8601 timestamp."
            },
            "required": false,
            "name": "created_after",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Only taps recorded strictly before this ISO-8601 timestamp."
            },
            "required": false,
            "name": "created_before",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": ["true", "false"],
              "description": "Set `true` to return taps of test chips instead. Defaults to `false`: only taps of encoded chips on physical items are returned."
            },
            "required": false,
            "name": "is_test",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of taps.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListTapsResponse"
                },
                "example": {
                  "taps": [
                    {
                      "id": "00000000-0000-4000-8000-000000000001",
                      "chip_id": "ABCDEF0123",
                      "unit_id": "22222222-2222-4222-8222-222222222222",
                      "count": 4,
                      "created_at": "2026-08-01T12:00:00.000Z"
                    }
                  ],
                  "pagination": {
                    "limit": 50,
                    "has_more": false,
                    "next_cursor": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/test-helpers/taps": {
      "post": {
        "operationId": "createTestTap",
        "summary": "Generate an e-value for a test chip",
        "description": "Returns a fresh `e` value for a test chip. POST the returned `e` to `/v1/taps` to test your integration end-to-end. Only valid for test chips, and only with a secret key.",
        "tags": ["Test helpers"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTestTapRequest"
              },
              "example": {
                "chip_id": "ABCDEF0123",
                "count": 5
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated e-value and count.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateTestTapResponse"
                },
                "example": {
                  "chip_id": "ABCDEF0123",
                  "e": "C78566198547116F3A715DC1C62AF96F",
                  "count": 5
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `chip.not_a_test_chip` | A tap simulation was attempted on a real chip. Simulation is only available for test chips. |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["chip.not_a_test_chip", "validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.not_a_test_chip": {
                    "summary": "A tap simulation was attempted on a real chip. Simulation is only available for test chips.",
                    "value": {
                      "error": {
                        "code": "chip.not_a_test_chip",
                        "message": "A tap simulation was attempted on a real chip. Simulation is only available for test chips.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-not-a-test-chip"
                      }
                    }
                  },
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted.\n\n| Error code | When |\n| --- | --- |\n| `auth.forbidden` | Credential is valid but does not have access to the requested resource or action. |",
            "x-error-codes": ["auth.forbidden"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `chip.not_found` | The chip ID does not exist in your organization. |",
            "x-error-codes": ["chip.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.not_found": {
                    "summary": "The chip ID does not exist in your organization.",
                    "value": {
                      "error": {
                        "code": "chip.not_found",
                        "message": "The chip ID does not exist in your organization.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-not-found"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/units/{unit_id}/chip-replacements/{replacement_id}": {
      "get": {
        "operationId": "getChipReplacement",
        "summary": "Get a chip replacement",
        "description": "Returns the status of a chip replacement. Poll until status is `confirmed`.",
        "tags": ["Chips"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "unit_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "replacement_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The chip replacement.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChipReplacement"
                },
                "example": {
                  "id": "00000000-0000-4000-8000-000000000000",
                  "unit_id": "22222222-2222-4222-8222-222222222222",
                  "old_chip_id": "ABCDEF0122",
                  "new_chip_id": "ABCDEF0123",
                  "status": "confirmed",
                  "created_at": "2026-06-15T12:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `chip_replacement.not_found` | No replacement with that ID exists for the unit. |",
            "x-error-codes": ["chip_replacement.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip_replacement.not_found": {
                    "summary": "No replacement with that ID exists for the unit.",
                    "value": {
                      "error": {
                        "code": "chip_replacement.not_found",
                        "message": "No replacement with that ID exists for the unit.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-replacement-not-found"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/units/{unit_id}/chip-replacements": {
      "post": {
        "operationId": "createChipReplacement",
        "summary": "Replace a unit's chip",
        "description": "Pairs a replacement chip to the unit and retires the currently paired one. The replacement chip must already exist and not be paired to any unit. Available until the unit is claimed; afterwards the chip is permanently locked. Poll GET /v1/units/{unit_id}/chip-replacements/{replacement_id} for status.",
        "tags": ["Chips"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "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,
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateChipReplacementRequest"
              },
              "example": {
                "new_chip_id": "ABCDEF0123"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created replacement.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChipReplacement"
                },
                "example": {
                  "id": "00000000-0000-4000-8000-000000000000",
                  "unit_id": "22222222-2222-4222-8222-222222222222",
                  "old_chip_id": "ABCDEF0122",
                  "new_chip_id": "ABCDEF0123",
                  "status": "prepared",
                  "created_at": "2026-06-15T12:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `chip.not_found` | The chip ID does not exist in your organization. |\n| `unit.not_found` | The unit ID does not exist in your organization. |",
            "x-error-codes": ["chip.not_found", "unit.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip.not_found": {
                    "summary": "The chip ID does not exist in your organization.",
                    "value": {
                      "error": {
                        "code": "chip.not_found",
                        "message": "The chip ID does not exist in your organization.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-not-found"
                      }
                    }
                  },
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The request conflicts with the resource's current state.\n\n| Error code | When |\n| --- | --- |\n| `chip_replacement.in_progress` | A replacement is already open for this unit. Poll it until it settles. |\n| `chip_replacement.locked` | The unit has been claimed - its chip is permanently locked and can no longer be replaced. |\n| `chip.already_paired` | The chip is already paired to a unit and cannot be paired again. |\n| `claim.in_progress` | A claim is already open for this unit. Wait for it to settle before creating another. |\n| `collection.not_active` | The collection has not finished provisioning. Wait until its status is `active`. |\n| `idempotency.in_progress` | A request with this `Idempotency-Key` is already in progress. |\n| `idempotency.key_conflict` | The `Idempotency-Key` was previously used with a different request body. |\n| `unit.not_minted` | The unit has not been issued yet. Pair a chip and wait for `token.status: \"active\"` first. |",
            "x-error-codes": [
              "chip_replacement.in_progress",
              "chip_replacement.locked",
              "chip.already_paired",
              "claim.in_progress",
              "collection.not_active",
              "idempotency.in_progress",
              "idempotency.key_conflict",
              "unit.not_minted"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "chip_replacement.in_progress": {
                    "summary": "A replacement is already open for this unit. Poll it until it settles.",
                    "value": {
                      "error": {
                        "code": "chip_replacement.in_progress",
                        "message": "A replacement is already open for this unit. Poll it until it settles.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-replacement-in-progress"
                      }
                    }
                  },
                  "chip_replacement.locked": {
                    "summary": "The unit has been claimed - its chip is permanently locked and can no longer be replaced.",
                    "value": {
                      "error": {
                        "code": "chip_replacement.locked",
                        "message": "The unit has been claimed - its chip is permanently locked and can no longer be replaced.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-replacement-locked"
                      }
                    }
                  },
                  "chip.already_paired": {
                    "summary": "The chip is already paired to a unit and cannot be paired again.",
                    "value": {
                      "error": {
                        "code": "chip.already_paired",
                        "message": "The chip is already paired to a unit and cannot be paired again.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/chip-already-paired"
                      }
                    }
                  },
                  "claim.in_progress": {
                    "summary": "A claim is already open for this unit. Wait for it to settle before creating another.",
                    "value": {
                      "error": {
                        "code": "claim.in_progress",
                        "message": "A claim is already open for this unit. Wait for it to settle before creating another.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/claim-in-progress"
                      }
                    }
                  },
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/units/{unit_id}/claims/{claim_id}": {
      "get": {
        "operationId": "getClaim",
        "summary": "Get a claim",
        "description": "Returns the status of a claim. Accepts either an API key or a chip-tap session token. Poll until status is `claimed`.",
        "tags": ["Claims"],
        "security": [
          {
            "ApiKeyBearer": []
          },
          {
            "SessionTokenBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "unit_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "claim_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The claim.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Claim"
                },
                "example": {
                  "id": "00000000-0000-4000-8000-000000000000",
                  "unit_id": "22222222-2222-4222-8222-222222222222",
                  "to": "0x1111111111111111111111111111111111111111",
                  "from": "0x2222222222222222222222222222222222222222",
                  "status": "claiming",
                  "created_at": "2026-06-15T12:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted.\n\n| Error code | When |\n| --- | --- |\n| `auth.forbidden` | Credential is valid but does not have access to the requested resource or action. |\n| `session_token.wrong_chip` | The session token is bound to a different chip than the one in the request. |",
            "x-error-codes": ["auth.forbidden", "session_token.wrong_chip"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `claim.not_found` | No claim with that ID exists for the unit. |",
            "x-error-codes": ["claim.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "claim.not_found": {
                    "summary": "No claim with that ID exists for the unit.",
                    "value": {
                      "error": {
                        "code": "claim.not_found",
                        "message": "No claim with that ID exists for the unit.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/claim-not-found"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/units/{unit_id}/claims": {
      "post": {
        "operationId": "createClaim",
        "summary": "Claim a unit",
        "description": "Hands a unit to a recipient address, authorized by the tap session token. `execution:\"endstate_relay\"` submits the claim for you; `execution:\"client_broadcast\"` returns a claim payload for you to submit. Poll GET /v1/units/{unit_id}/claims/{claim_id} for status. Send an `Idempotency-Key` header to make a retry safe: the same key replays the original response instead of creating a second claim.",
        "tags": ["Claims"],
        "security": [
          {
            "SessionTokenBearer": []
          }
        ],
        "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,
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateClaimRequest"
              },
              "example": {
                "to": "0x1111111111111111111111111111111111111111",
                "execution": "endstate_relay"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created claim.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Claim"
                },
                "example": {
                  "id": "00000000-0000-4000-8000-000000000000",
                  "unit_id": "22222222-2222-4222-8222-222222222222",
                  "to": "0x1111111111111111111111111111111111111111",
                  "from": "0x2222222222222222222222222222222222222222",
                  "status": "claiming",
                  "created_at": "2026-06-15T12:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted.\n\n| Error code | When |\n| --- | --- |\n| `auth.forbidden` | Credential is valid but does not have access to the requested resource or action. |\n| `session_token.wrong_chip` | The session token is bound to a different chip than the one in the request. |",
            "x-error-codes": ["auth.forbidden", "session_token.wrong_chip"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `unit.not_found` | The unit ID does not exist in your organization. |",
            "x-error-codes": ["unit.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The request conflicts with the resource's current state.\n\n| Error code | When |\n| --- | --- |\n| `claim.already_to_recipient` | The unit is already owned by, or being claimed to, that address. |\n| `claim.in_progress` | A claim is already open for this unit. Wait for it to settle before creating another. |\n| `claim.owner_unknown` | The unit's current owner cannot be determined yet. Retry shortly. |\n| `idempotency.in_progress` | A request with this `Idempotency-Key` is already in progress. |\n| `idempotency.key_conflict` | The `Idempotency-Key` was previously used with a different request body. |\n| `unit.not_minted` | The unit has not been issued yet. Pair a chip and wait for `token.status: \"active\"` first. |",
            "x-error-codes": [
              "claim.already_to_recipient",
              "claim.in_progress",
              "claim.owner_unknown",
              "idempotency.in_progress",
              "idempotency.key_conflict",
              "unit.not_minted"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "claim.already_to_recipient": {
                    "summary": "The unit is already owned by, or being claimed to, that address.",
                    "value": {
                      "error": {
                        "code": "claim.already_to_recipient",
                        "message": "The unit is already owned by, or being claimed to, that address.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/claim-already-to-recipient"
                      }
                    }
                  },
                  "claim.in_progress": {
                    "summary": "A claim is already open for this unit. Wait for it to settle before creating another.",
                    "value": {
                      "error": {
                        "code": "claim.in_progress",
                        "message": "A claim is already open for this unit. Wait for it to settle before creating another.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/claim-in-progress"
                      }
                    }
                  },
                  "claim.owner_unknown": {
                    "summary": "The unit's current owner cannot be determined yet. Retry shortly.",
                    "value": {
                      "error": {
                        "code": "claim.owner_unknown",
                        "message": "The unit's current owner cannot be determined yet. Retry shortly.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/claim-owner-unknown"
                      }
                    }
                  },
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/units/{unit_id}": {
      "get": {
        "operationId": "getUnit",
        "summary": "Fetch a unit",
        "description": "Returns a single unit by id, including its collection and issuance status. Accepts either an API key or a chip-tap session token; a session token can read only the unit it was issued for. Poll `collection.token.status` until it is `active`.",
        "tags": ["Units"],
        "security": [
          {
            "ApiKeyBearer": []
          },
          {
            "SessionTokenBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Unit id.",
              "example": "33333333-3333-3333-3333-333333333333"
            },
            "required": true,
            "name": "unit_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The unit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unit"
                },
                "example": {
                  "id": "33333333-3333-3333-3333-333333333333",
                  "external_id": "unit-001",
                  "name": "Example Unit",
                  "attributes": {
                    "custom_attribute": "value"
                  },
                  "created_at": "2026-05-16T12:00:00.000Z",
                  "redirect_url": null,
                  "collection": {
                    "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                    "name": "Example Collection",
                    "external_id": "collection-001",
                    "contract": {
                      "address": "0x1111111111111111111111111111111111111111",
                      "chain_id": 84532,
                      "status": "active"
                    },
                    "token": {
                      "status": "active",
                      "serial": 5
                    }
                  },
                  "chips": [
                    {
                      "chip_id": "ABCDEF0123",
                      "is_test": false,
                      "scan_count": 0,
                      "created_at": "2026-05-16T12:00:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted.\n\n| Error code | When |\n| --- | --- |\n| `auth.forbidden` | Credential is valid but does not have access to the requested resource or action. |\n| `session_token.wrong_chip` | The session token is bound to a different chip than the one in the request. |",
            "x-error-codes": ["auth.forbidden", "session_token.wrong_chip"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `unit.not_found` | The unit ID does not exist in your organization. |",
            "x-error-codes": ["unit.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "updateUnit",
        "summary": "Update a unit",
        "description": "Updates a unit's tap redirect URL. Send null to clear.",
        "tags": ["Units"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Unit id.",
              "example": "33333333-3333-3333-3333-333333333333"
            },
            "required": true,
            "name": "unit_id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateUnitRequest"
              },
              "example": {
                "redirect_url": "https://brand.example/p"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated unit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unit"
                },
                "example": {
                  "id": "33333333-3333-3333-3333-333333333333",
                  "external_id": "unit-001",
                  "name": "Example Unit",
                  "attributes": {
                    "custom_attribute": "value"
                  },
                  "created_at": "2026-05-16T12:00:00.000Z",
                  "redirect_url": "https://brand.example/p",
                  "collection": {
                    "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                    "name": "Example Collection",
                    "external_id": "collection-001",
                    "contract": {
                      "address": "0x1111111111111111111111111111111111111111",
                      "chain_id": 84532,
                      "status": "active"
                    },
                    "token": {
                      "status": "active",
                      "serial": 5
                    }
                  },
                  "chips": []
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `unit.not_found` | The unit ID does not exist in your organization. |",
            "x-error-codes": ["unit.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/units/{unit_id}/transfers/{transfer_id}": {
      "get": {
        "operationId": "getTransfer",
        "summary": "Get a transfer",
        "description": "Returns the status of a transfer. Accepts either an API key or a chip-tap session token. Poll until status is `confirmed`.",
        "tags": ["Transfers"],
        "security": [
          {
            "ApiKeyBearer": []
          },
          {
            "SessionTokenBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "unit_id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "transfer_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The transfer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transfer"
                },
                "example": {
                  "id": "00000000-0000-4000-8000-000000000000",
                  "unit_id": "22222222-2222-4222-8222-222222222222",
                  "to": "0x1111111111111111111111111111111111111111",
                  "from": "0x2222222222222222222222222222222222222222",
                  "status": "prepared",
                  "created_at": "2026-06-15T12:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted.\n\n| Error code | When |\n| --- | --- |\n| `auth.forbidden` | Credential is valid but does not have access to the requested resource or action. |\n| `session_token.wrong_chip` | The session token is bound to a different chip than the one in the request. |",
            "x-error-codes": ["auth.forbidden", "session_token.wrong_chip"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `transfer.not_found` | No transfer with that ID exists for the unit. |",
            "x-error-codes": ["transfer.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "transfer.not_found": {
                    "summary": "No transfer with that ID exists for the unit.",
                    "value": {
                      "error": {
                        "code": "transfer.not_found",
                        "message": "No transfer with that ID exists for the unit.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/transfer-not-found"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/units/{unit_id}/transfers": {
      "post": {
        "operationId": "createTransfer",
        "summary": "Transfer a unit",
        "description": "Moves a unit from its current owner to a new owner, authorized by the tap session token. Returns a transfer payload that the unit's current owner submits to complete the transfer. Poll GET /v1/units/{unit_id}/transfers/{transfer_id} until status is `confirmed`. Send an `Idempotency-Key` header to make a retry safe: the same key replays the original response instead of preparing a second transfer.",
        "tags": ["Transfers"],
        "security": [
          {
            "SessionTokenBearer": []
          }
        ],
        "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,
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTransferRequest"
              },
              "example": {
                "to": "0x1111111111111111111111111111111111111111"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created transfer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transfer"
                },
                "example": {
                  "id": "00000000-0000-4000-8000-000000000000",
                  "unit_id": "22222222-2222-4222-8222-222222222222",
                  "to": "0x1111111111111111111111111111111111111111",
                  "from": "0x2222222222222222222222222222222222222222",
                  "status": "prepared",
                  "transaction": {
                    "to": "0x3333333333333333333333333333333333333333",
                    "data": "0xa9059cbb"
                  },
                  "created_at": "2026-06-15T12:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted.\n\n| Error code | When |\n| --- | --- |\n| `auth.forbidden` | Credential is valid but does not have access to the requested resource or action. |\n| `session_token.wrong_chip` | The session token is bound to a different chip than the one in the request. |",
            "x-error-codes": ["auth.forbidden", "session_token.wrong_chip"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `unit.not_found` | The unit ID does not exist in your organization. |",
            "x-error-codes": ["unit.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The request conflicts with the resource's current state.\n\n| Error code | When |\n| --- | --- |\n| `collection.not_active` | The collection has not finished provisioning. Wait until its status is `active`. |\n| `idempotency.in_progress` | A request with this `Idempotency-Key` is already in progress. |\n| `idempotency.key_conflict` | The `Idempotency-Key` was previously used with a different request body. |\n| `transfer.already_to_recipient` | The unit is already held by that address. |\n| `transfer.in_progress` | A transfer is already open for this unit. Wait for it to settle before starting another. |\n| `transfer.owner_unknown` | The unit's current owner cannot be determined yet. Retry shortly. |\n| `unit.not_minted` | The unit has not been issued yet. Pair a chip and wait for `token.status: \"active\"` first. |",
            "x-error-codes": [
              "collection.not_active",
              "idempotency.in_progress",
              "idempotency.key_conflict",
              "transfer.already_to_recipient",
              "transfer.in_progress",
              "transfer.owner_unknown",
              "unit.not_minted"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  },
                  "transfer.already_to_recipient": {
                    "summary": "The unit is already held by that address.",
                    "value": {
                      "error": {
                        "code": "transfer.already_to_recipient",
                        "message": "The unit is already held by that address.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/transfer-already-to-recipient"
                      }
                    }
                  },
                  "transfer.in_progress": {
                    "summary": "A transfer is already open for this unit. Wait for it to settle before starting another.",
                    "value": {
                      "error": {
                        "code": "transfer.in_progress",
                        "message": "A transfer is already open for this unit. Wait for it to settle before starting another.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/transfer-in-progress"
                      }
                    }
                  },
                  "transfer.owner_unknown": {
                    "summary": "The unit's current owner cannot be determined yet. Retry shortly.",
                    "value": {
                      "error": {
                        "code": "transfer.owner_unknown",
                        "message": "The unit's current owner cannot be determined yet. Retry shortly.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/transfer-owner-unknown"
                      }
                    }
                  },
                  "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/units": {
      "get": {
        "operationId": "listUnits",
        "summary": "List units",
        "description": "Returns a cursor-paginated list of units, including each unit's collection and issuance status. Use `external_id` to filter to one caller-supplied identifier.",
        "tags": ["Units"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "description": "Maximum number of records to return. Defaults to 50; maximum 100."
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Opaque cursor from the previous page."
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Filter to units with this external identifier."
            },
            "required": false,
            "name": "external_id",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of units.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnitsListResponse"
                },
                "example": {
                  "units": [
                    {
                      "id": "33333333-3333-3333-3333-333333333333",
                      "external_id": "unit-001",
                      "name": "Example Unit",
                      "attributes": {
                        "custom_attribute": "value"
                      },
                      "created_at": "2026-05-16T12:00:00.000Z",
                      "redirect_url": null,
                      "collection": {
                        "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                        "name": "Example Collection",
                        "external_id": "collection-001",
                        "contract": {
                          "address": "0x1111111111111111111111111111111111111111",
                          "chain_id": 84532,
                          "status": "active"
                        },
                        "token": {
                          "status": "active",
                          "serial": 5
                        }
                      },
                      "chips": [
                        {
                          "chip_id": "ABCDEF0123",
                          "is_test": false,
                          "scan_count": 0,
                          "created_at": "2026-05-16T12:00:00.000Z"
                        }
                      ]
                    }
                  ],
                  "pagination": {
                    "limit": 50,
                    "has_more": false,
                    "next_cursor": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createUnit",
        "summary": "Create a unit",
        "description": "Creates a unit in your organization, inside the given collection.",
        "tags": ["Units"],
        "security": [
          {
            "ApiKeyBearer": []
          }
        ],
        "parameters": [
          {
            "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,
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateUnitRequest"
              },
              "example": {
                "external_id": "unit-001",
                "name": "Example Unit",
                "attributes": {
                  "custom_attribute": "value"
                },
                "collection_id": "8e1a7f50-90ab-4cde-8012-3456789abcde"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created unit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unit"
                },
                "example": {
                  "id": "33333333-3333-3333-3333-333333333333",
                  "external_id": "unit-001",
                  "name": "Example Unit",
                  "attributes": {
                    "custom_attribute": "value"
                  },
                  "created_at": "2026-05-16T12:00:00.000Z",
                  "redirect_url": null,
                  "collection": {
                    "id": "8e1a7f50-90ab-4cde-8012-3456789abcde",
                    "name": "Example Collection",
                    "external_id": "collection-001",
                    "contract": {
                      "address": "0x1111111111111111111111111111111111111111",
                      "chain_id": 84532,
                      "status": "active"
                    },
                    "token": {
                      "status": "pending",
                      "serial": null
                    }
                  },
                  "chips": []
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist within your organization.\n\n| Error code | When |\n| --- | --- |\n| `collection.not_found` | The collection ID does not exist in your organization. |",
            "x-error-codes": ["collection.not_found"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "collection.not_found": {
                    "summary": "The collection ID does not exist in your organization.",
                    "value": {
                      "error": {
                        "code": "collection.not_found",
                        "message": "The collection ID does not exist in your organization.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/collection-not-found"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The request conflicts with the resource's current state.\n\n| Error code | When |\n| --- | --- |\n| `idempotency.in_progress` | A request with this `Idempotency-Key` is already in progress. |\n| `idempotency.key_conflict` | The `Idempotency-Key` was previously used with a different request body. |\n| `unit.already_exists` | A unit with the supplied `external_id` already exists in your organization. |",
            "x-error-codes": [
              "idempotency.in_progress",
              "idempotency.key_conflict",
              "unit.already_exists"
            ],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "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.already_exists": {
                    "summary": "A unit with the supplied `external_id` already exists in your organization.",
                    "value": {
                      "error": {
                        "code": "unit.already_exists",
                        "message": "A unit with the supplied `external_id` already exists in your organization.",
                        "request_id": "req_8e1a7f50-90ab-4cde-f012-3456789abcde",
                        "doc_url": "https://docs.endstate.io/errors/unit-already-exists"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/wallet/sessions": {
      "post": {
        "operationId": "createWalletSession",
        "summary": "Start a wallet session",
        "description": "Exchanges the single-use identity credential presented in the `Authorization: Bearer` header for a wallet session belonging to the customer it identifies. Once the exchange accepts the credential it is spent, whether or not the rest of the exchange succeeds. Customers who have not verified an email address have no wallet and are refused. The Endstate wallet frame calls this endpoint; your own code does not.",
        "tags": ["Wallet"],
        "security": [
          {
            "WalletIdentityBearer": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWalletSessionRequest"
              },
              "example": {
                "publishable_key": "end_pk_live_0123456789abcdef0123456789abcdef"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The session was started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateWalletSessionResponse"
                },
                "example": {
                  "token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiJ9...",
                  "session_token": "end_wsess_AbCd_example_token",
                  "expires_at": 1755640000,
                  "chain_id": 8453
                }
              }
            }
          },
          "400": {
            "description": "The request was malformed or failed validation.\n\n| Error code | When |\n| --- | --- |\n| `validation.failed` | The request failed schema validation. See `error.details` for per-field issues. |",
            "x-error-codes": ["validation.failed"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed.\n\n| Error code | When |\n| --- | --- |\n| `auth.unauthorized` | Credential is missing or malformed. |",
            "x-error-codes": ["auth.unauthorized"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted.\n\n| Error code | When |\n| --- | --- |\n| `auth.forbidden` | Credential is valid but does not have access to the requested resource or action. |",
            "x-error-codes": ["auth.forbidden"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.\n\n| Error code | When |\n| --- | --- |\n| `rate_limit.exceeded` | Per-key rate limit exceeded. |",
            "x-error-codes": ["rate_limit.exceeded"],
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our end.\n\n| Error code | When |\n| --- | --- |\n| `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": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "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"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {}
}
