{
  "openapi": "3.0.3",
  "info": {
    "title": "WalletPass API",
    "version": "2.0.0",
    "description": "Create, update, and push live wallet passes to Apple Wallet and Google Wallet. Design your template in the portal, then issue personalised passes via API.",
    "contact": {
      "url": "https://walletpassapi.com/docs"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.walletpassapi.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Workspace API Key (wk_...)",
        "description": "Workspace API key — authenticates pass operations scoped to a specific workspace."
      },
      "accountKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Account API Key (sk_...)",
        "description": "Account API key — authenticates account and workspace management."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "example": "invalid_request"
          },
          "message": {
            "type": "string",
            "example": "template_id is required"
          }
        }
      },
      "PassField": {
        "type": "object",
        "required": [
          "key",
          "value"
        ],
        "properties": {
          "key": {
            "type": "string",
            "example": "points"
          },
          "label": {
            "type": "string",
            "example": "Points"
          },
          "value": {
            "type": "string",
            "example": "1,200"
          }
        }
      },
      "Barcode": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "qr",
              "aztec",
              "pdf417",
              "code128"
            ],
            "example": "qr"
          },
          "message": {
            "type": "string",
            "example": "MEMBER-00421"
          },
          "alt_text": {
            "type": "string",
            "description": "Apple Wallet only",
            "example": "MEMBER-00421"
          }
        }
      },
      "Location": {
        "type": "object",
        "required": [
          "latitude",
          "longitude"
        ],
        "properties": {
          "latitude": {
            "type": "number",
            "example": 37.7749
          },
          "longitude": {
            "type": "number",
            "example": -122.4194
          },
          "relevant_text": {
            "type": "string",
            "example": "Welcome!"
          }
        }
      },
      "VariableSchema": {
        "type": "array",
        "description": "Variable definitions from the template",
        "items": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string",
              "example": "member_name"
            },
            "type": {
              "type": "string",
              "enum": [
                "text",
                "image"
              ],
              "example": "text"
            },
            "required": {
              "type": "boolean"
            }
          }
        }
      },
      "TemplateResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "tmpl_a1b2c3d4"
          },
          "name": {
            "type": "string",
            "example": "Loyalty Card"
          },
          "pass_style": {
            "type": "string",
            "enum": [
              "storeCard",
              "coupon",
              "eventTicket",
              "boardingPass",
              "generic"
            ]
          },
          "variable_schema": {
            "$ref": "#/components/schemas/VariableSchema"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_pushed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "PassResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "pass_01j9abc..."
          },
          "template_id": {
            "type": "string",
            "example": "tmpl_a1b2c3d4",
            "nullable": true
          },
          "serial_number": {
            "type": "string",
            "example": "SN-0001"
          },
          "pass_type": {
            "type": "string",
            "example": "storeCard",
            "nullable": true
          },
          "email": {
            "type": "string",
            "format": "email",
            "nullable": true
          },
          "first_name": {
            "type": "string",
            "nullable": true
          },
          "last_name": {
            "type": "string",
            "nullable": true
          },
          "sms_mobile": {
            "type": "string",
            "nullable": true
          },
          "external_id": {
            "type": "string",
            "nullable": true
          },
          "variables": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Variable values substituted into the template"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "device_count": {
            "type": "integer",
            "description": "Number of Apple Wallet devices with this pass installed"
          },
          "pkpass_url": {
            "type": "string",
            "format": "uri",
            "description": "Public link to download the .pkpass file"
          },
          "google_jwt": {
            "type": "string",
            "nullable": true
          },
          "google_save_url": {
            "type": "string",
            "format": "uri",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AccountResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "acct_01j9abc..."
          },
          "name": {
            "type": "string",
            "example": "Acme Corp"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "plan": {
            "type": "string",
            "example": "starter"
          },
          "trial_ends_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "api_key_prefix": {
            "type": "string",
            "example": "sk_a1b2c3d4",
            "description": "First chars of the sk_ key — for identification only"
          },
          "webhook_url": {
            "type": "string",
            "format": "uri",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WorkspaceResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "ws_01j9abc..."
          },
          "name": {
            "type": "string",
            "example": "Production"
          },
          "is_default": {
            "type": "boolean"
          },
          "operations_allocated": {
            "type": "integer",
            "example": 5000
          },
          "operations_used": {
            "type": "integer",
            "example": 120
          },
          "api_key_prefix": {
            "type": "string",
            "example": "wk_a1b2c3d4",
            "description": "First 11 chars of the workspace API key — for identification only"
          },
          "use_account_credentials": {
            "type": "boolean",
            "description": "When true, the workspace inherits credentials from its parent account"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "has_more": {
            "type": "boolean"
          }
        }
      }
    }
  },
  "paths": {
    "/api/v1/accounts": {
      "post": {
        "summary": "Create account",
        "description": "Create a new account and a default workspace. No authentication required. The `api_key` (sk_) is returned once — store it immediately.",
        "operationId": "createAccount",
        "security": [],
        "tags": [
          "Accounts"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "email"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "Acme Corp"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "hello@acme.com"
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "workspace_id": {
                      "type": "string"
                    },
                    "api_key": {
                      "type": "string",
                      "description": "sk_ account key — shown only once"
                    },
                    "webhook_secret": {
                      "type": "string",
                      "description": "Webhook signing secret — shown only once"
                    },
                    "portal_url": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Email already registered",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/accounts/me": {
      "get": {
        "summary": "Get account",
        "operationId": "getAccount",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "accountKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Account details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Update account",
        "operationId": "updateAccount",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "accountKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri",
                    "nullable": true
                  },
                  "rotate_webhook_secret": {
                    "type": "boolean",
                    "description": "Pass true to regenerate the webhook signing secret"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated account",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/AccountResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "webhook_secret": {
                          "type": "string",
                          "description": "Only present when rotate_webhook_secret was true"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete account",
        "description": "Permanently delete the account and all associated workspaces, passes, and device registrations. Irreversible.",
        "operationId": "deleteAccount",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "accountKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    },
                    "deleted_passes": {
                      "type": "integer"
                    },
                    "deleted_registrations": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/accounts/me/rotate-key": {
      "post": {
        "summary": "Rotate account API key",
        "description": "Invalidate the current sk_ key and return a new one. The new key is shown only once.",
        "operationId": "rotateAccountKey",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "accountKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "New key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "api_key": {
                      "type": "string",
                      "description": "New sk_ key — shown only once"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/accounts/me/credentials": {
      "put": {
        "summary": "Upload signing credentials",
        "description": "Upload Apple and/or Google signing credentials for account-level use. Credentials are encrypted at rest.",
        "operationId": "updateCredentials",
        "tags": [
          "Accounts"
        ],
        "security": [
          {
            "accountKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "apple_p12_cert": {
                    "type": "string",
                    "format": "byte",
                    "description": "Base64-encoded .p12 certificate"
                  },
                  "apple_p12_password": {
                    "type": "string",
                    "description": "Password for the .p12"
                  },
                  "apple_pass_type_identifier": {
                    "type": "string",
                    "example": "pass.com.example.app"
                  },
                  "apple_team_id": {
                    "type": "string",
                    "example": "ABCDE12345"
                  },
                  "apple_apns_private_key": {
                    "type": "string",
                    "format": "byte",
                    "description": "Base64-encoded APNs .p8 key"
                  },
                  "apple_apns_key_id": {
                    "type": "string"
                  },
                  "google_issuer_id": {
                    "type": "string"
                  },
                  "google_service_account_email": {
                    "type": "string",
                    "format": "email"
                  },
                  "google_service_account_key": {
                    "type": "string",
                    "format": "byte",
                    "description": "Base64-encoded service account JSON key"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credentials updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updated": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No recognised credential fields",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workspaces": {
      "get": {
        "summary": "List workspaces",
        "description": "Returns all workspaces and the account-level operations pool summary.",
        "operationId": "listWorkspaces",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "accountKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Workspace list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "workspaces": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WorkspaceResponse"
                      }
                    },
                    "total_ops": {
                      "type": "integer"
                    },
                    "allocated": {
                      "type": "integer"
                    },
                    "unallocated": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create workspace",
        "description": "Create a new workspace. Returns the workspace `wk_` API key once — store it immediately.",
        "operationId": "createWorkspace",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "accountKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "Production"
                  },
                  "operations_allocated": {
                    "type": "integer",
                    "default": 0,
                    "description": "Operations to allocate from the account pool"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Workspace created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "workspace": {
                      "$ref": "#/components/schemas/WorkspaceResponse"
                    },
                    "api_key": {
                      "type": "string",
                      "description": "wk_ key — shown only once"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workspaces/{id}": {
      "get": {
        "summary": "Get workspace",
        "operationId": "getWorkspace",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "accountKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Workspace",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "workspace": {
                      "$ref": "#/components/schemas/WorkspaceResponse"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Update workspace",
        "operationId": "updateWorkspace",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "accountKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "operations_allocated": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "workspace": {
                      "$ref": "#/components/schemas/WorkspaceResponse"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete workspace",
        "description": "Delete a workspace and all its passes. The default workspace cannot be deleted.",
        "operationId": "deleteWorkspace",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "accountKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "deleted_passes": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Cannot delete default workspace",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workspaces/{id}/rotate-key": {
      "post": {
        "summary": "Rotate workspace API key",
        "operationId": "rotateWorkspaceKey",
        "tags": [
          "Workspaces"
        ],
        "security": [
          {
            "accountKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "New key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "api_key": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/templates": {
      "get": {
        "summary": "List templates",
        "description": "List all pass templates in the workspace. Templates are designed in the portal; this endpoint lets you look up template IDs and variable schemas programmatically.",
        "operationId": "listTemplates",
        "tags": [
          "Templates"
        ],
        "responses": {
          "200": {
            "description": "Template list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TemplateResponse"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/templates/{id}": {
      "get": {
        "summary": "Get template",
        "operationId": "getTemplate",
        "tags": [
          "Templates"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/templates/{id}/passes": {
      "get": {
        "summary": "List passes for template",
        "description": "List all passes issued from a specific template, with full identity and variable fields.",
        "operationId": "listPassesForTemplate",
        "tags": [
          "Templates"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pass list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PassResponse"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Template not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/issue": {
      "post": {
        "summary": "Issue a pass",
        "description": "Issue a pass from a template with variable values. If a pass already exists for this `email` (or `external_id`), it is updated and pushed to any registered devices instead of creating a duplicate.",
        "operationId": "issuePass",
        "tags": [
          "Passes"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "template_id",
                  "email"
                ],
                "properties": {
                  "template_id": {
                    "type": "string",
                    "example": "tmpl_a1b2c3d4",
                    "description": "ID of the template to issue from"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "example": "jane@example.com"
                  },
                  "first_name": {
                    "type": "string",
                    "example": "Jane"
                  },
                  "last_name": {
                    "type": "string",
                    "example": "Smith"
                  },
                  "sms_mobile": {
                    "type": "string",
                    "example": "+447911123456"
                  },
                  "external_id": {
                    "type": "string",
                    "description": "Your own ID for this person — used for upsert instead of email when provided",
                    "example": "usr_999"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Labels for filtering/segmentation",
                    "example": [
                      "vip",
                      "london"
                    ]
                  },
                  "variables": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Variable values to substitute into the template. Keys must match the template variable schema.",
                    "example": {
                      "points": "1,250",
                      "tier": "Gold"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Existing pass updated and pushed to devices",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "serial_number": {
                      "type": "string"
                    },
                    "pkpass_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "updated": {
                      "type": "boolean",
                      "example": true
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Pass created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "serial_number": {
                      "type": "string"
                    },
                    "pkpass_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "google_jwt": {
                      "type": "string",
                      "nullable": true
                    },
                    "google_save_url": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or missing required variable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Operation limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Template not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/passes": {
      "get": {
        "summary": "List passes",
        "description": "List all passes in the workspace with full identity and variable fields.",
        "operationId": "listPasses",
        "tags": [
          "Passes"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pass list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PassResponse"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/passes/{id}": {
      "get": {
        "summary": "Get pass",
        "description": "Retrieve a pass with full identity, variable, and device fields.",
        "operationId": "getPass",
        "tags": [
          "Passes"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pass",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PassResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Pass not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Update pass",
        "description": "Update a pass. Pass `variables` to re-substitute from the template and push the update to installed devices. Pass identity fields (`email`, `first_name`, etc.) to update the person record. Pass `silent: true` to skip the push notification.",
        "operationId": "updatePass",
        "tags": [
          "Passes"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "variables": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Updated variable values — re-substitutes from template and pushes to devices",
                    "example": {
                      "points": "2,000"
                    }
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "first_name": {
                    "type": "string"
                  },
                  "last_name": {
                    "type": "string"
                  },
                  "sms_mobile": {
                    "type": "string"
                  },
                  "external_id": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "silent": {
                    "type": "boolean",
                    "description": "When true, skip the Apple Wallet push notification",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated pass",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PassResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "push_sent_to": {
                          "type": "integer",
                          "description": "Number of Apple devices notified"
                        },
                        "push_errors": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Any push errors (only present if errors occurred)"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Operation limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Pass not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete pass",
        "description": "Delete a pass and send a removal push to registered Apple devices.",
        "operationId": "deletePass",
        "tags": [
          "Passes"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    },
                    "push_sent_to": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Pass not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/passes/{id}/notify": {
      "post": {
        "summary": "Send push notification",
        "description": "Push a message to the pass holder. The message appears on the back of the Apple Wallet pass and triggers a lock-screen notification.",
        "operationId": "notifyPass",
        "tags": [
          "Passes"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "message"
                ],
                "properties": {
                  "message": {
                    "type": "string",
                    "example": "Your points balance has been updated!"
                  },
                  "label": {
                    "type": "string",
                    "example": "Last Message",
                    "description": "Field label shown on the back of the pass"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Notification sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "push_sent_to": {
                      "type": "integer"
                    },
                    "pkpass_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "google_save_url": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "message is required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Operation limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Pass not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/passes/{id}/pkpass": {
      "get": {
        "summary": "Download .pkpass",
        "description": "Download the signed Apple Wallet .pkpass binary. This endpoint is public — no authentication required. Use the URL as the pass install link.",
        "operationId": "downloadPkpass",
        "tags": [
          "Passes"
        ],
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Signed .pkpass file",
            "content": {
              "application/vnd.apple.pkpass": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Pass not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Apple credentials not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/passes/{id}/google-jwt": {
      "get": {
        "summary": "Get Google Wallet JWT",
        "description": "Get a fresh Google Wallet JWT and save URL for embedding in a \"Save to Google Wallet\" button.",
        "operationId": "googleJwt",
        "tags": [
          "Passes"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Google Wallet URLs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "google_jwt": {
                      "type": "string"
                    },
                    "google_save_url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Pass not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Google credentials not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/passes/batch": {
      "post": {
        "summary": "Batch issue passes",
        "description": "Queue up to 1,000 passes for async creation. Each item in the `passes` array should be an issue request body (template_id, email, variables, etc.). Poll the returned `job_id` for status.",
        "operationId": "batchCreate",
        "tags": [
          "Batch"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "passes"
                ],
                "properties": {
                  "passes": {
                    "type": "array",
                    "maxItems": 1000,
                    "items": {
                      "type": "object"
                    },
                    "description": "Array of issue request bodies"
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "URL to notify on completion (overrides account webhook)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "example": "queued"
                    },
                    "pass_count": {
                      "type": "integer"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/passes/batch/{job_id}": {
      "get": {
        "summary": "Get batch job status",
        "description": "Poll a batch job for status and per-item results.",
        "operationId": "getBatchJob",
        "tags": [
          "Batch"
        ],
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Batch job",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "queued",
                        "processing",
                        "completed",
                        "failed"
                      ]
                    },
                    "pass_count": {
                      "type": "integer"
                    },
                    "completed_count": {
                      "type": "integer"
                    },
                    "failed_count": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "index": {
                            "type": "integer"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "ok",
                              "error"
                            ]
                          },
                          "id": {
                            "type": "string",
                            "nullable": true
                          },
                          "error": {
                            "type": "string",
                            "nullable": true
                          },
                          "error_message": {
                            "type": "string",
                            "nullable": true
                          }
                        }
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "completed_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Job not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}