{
  "openapi": "3.1.0",
  "info": {
    "title": "API Reference",
    "version": "1.0.0",
    "description": "ProofAge is an identity verification and age estimation API. Use this API to create verification sessions, upload media (selfies and documents), and receive decisions via webhooks."
  },
  "servers": [
    {
      "url": "https://api.proofage.net/v1",
      "description": "API"
    }
  ],
  "security": [
    {
      "apiKey": [],
      "hmacSignature": []
    }
  ],
  "paths": {
    "/verifications/{verification}/media": {
      "post": {
        "operationId": "uploadMedia",
        "description": "Uploads a selfie or document image to the verification session.\nConsent must be accepted before uploading. The file is validated\nfor quality (face detection, blur, brightness for selfies;\ndocument detection, readability for documents).\n\nAnswers `200` with an empty body; a failed quality check answers `422`.",
        "summary": "Upload media",
        "tags": [
          "Media"
        ],
        "parameters": [
          {
            "name": "verification",
            "in": "path",
            "required": true,
            "description": "The verification ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/UploadMediaRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The media was stored. The body is empty.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "examples": [
                    ""
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ModelNotFoundException"
          },
          "422": {
            "description": "The image failed a quality check; the code says which.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "code": "FACE_NOT_FOUND",
                      "message": "Face validation failed. Please upload a clear, well-lit selfie with your face fully visible."
                    }
                  ],
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "code",
                    "message"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/verifications/{verification}/media/{media}": {
      "get": {
        "operationId": "downloadVerificationMedia",
        "description": "Streams the bytes of a selfie or document image belonging to the\nverification, served by ProofAge under the same API key and HMAC\nsignature as every other endpoint.\n\nThe response body is the image itself; `Content-Type` carries its MIME\ntype. Media that has been purged, has passed its retention window, or\ndoes not belong to this verification answers 404.",
        "summary": "Download media",
        "tags": [
          "Media"
        ],
        "parameters": [
          {
            "name": "verification",
            "in": "path",
            "required": true,
            "description": "The verification ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "media",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The media file itself. Content-Type carries its MIME type.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Media does not belong to this verification, or is no longer available.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "error": {
                        "code": "MEDIA_NOT_FOUND",
                        "message": "Media not found."
                      }
                    }
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string"
                        },
                        "message": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "code",
                        "message"
                      ]
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationException"
          }
        }
      }
    },
    "/verifications": {
      "post": {
        "operationId": "createVerification",
        "description": "Creates a new verification session and returns a URL for the end-user to complete verification.",
        "summary": "Create a verification session",
        "tags": [
          "Verification"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreVerificationRequest"
              }
            }
          }
        },
        "responses": {
          "422": {
            "$ref": "#/components/responses/ValidationException"
          },
          "201": {
            "description": "The verification session, with the url the person opens to complete it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "external_id": "user-123",
                      "external_metadata": {
                        "plan": "premium"
                      },
                      "redirect_url": "https://example.com/callback",
                      "status": "created",
                      "reason": null,
                      "duplicate_check": {
                        "checked": false,
                        "duplicate_count": 0,
                        "duplicates": []
                      },
                      "erasure": null,
                      "consent_accepted_at": null,
                      "created_at": "2026-03-19T12:00:00+00:00",
                      "updated_at": "2026-03-19T12:00:00+00:00",
                      "url": "https://idv.proofage.net/v/eyJ..."
                    }
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "external_id": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "external_metadata": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "additionalProperties": {}
                    },
                    "redirect_url": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "status": {
                      "type": "string"
                    },
                    "reason": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "duplicate_check": {
                      "type": "object",
                      "properties": {
                        "checked": {
                          "type": "boolean"
                        },
                        "duplicate_count": {
                          "type": "integer"
                        },
                        "duplicates": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "verification_id": {
                                "type": "string"
                              },
                              "external_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "similarity_score": {
                                "type": "number"
                              },
                              "verified_at": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            },
                            "required": [
                              "verification_id",
                              "external_id",
                              "similarity_score",
                              "verified_at"
                            ]
                          }
                        }
                      },
                      "required": [
                        "checked",
                        "duplicate_count",
                        "duplicates"
                      ]
                    },
                    "erasure": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "erased_at": {
                          "type": "string"
                        },
                        "scope": {
                          "type": "string"
                        },
                        "reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "requested_via": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "erased_at",
                        "scope",
                        "reason",
                        "requested_via"
                      ]
                    },
                    "consent_accepted_at": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "created_at": {
                      "type": "string"
                    },
                    "updated_at": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "external_id",
                    "external_metadata",
                    "redirect_url",
                    "status",
                    "reason",
                    "duplicate_check",
                    "erasure",
                    "consent_accepted_at",
                    "created_at",
                    "updated_at",
                    "url"
                  ]
                }
              }
            }
          },
          "402": {
            "description": "A live workspace has no payment method.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "code": "PAYMENT_METHOD_REQUIRED",
                      "message": "A payment method is required to create verifications.",
                      "free_verifications_remaining": 0,
                      "trial_ends_at": "2026-04-03T12:00:00+00:00",
                      "trial_active": false
                    }
                  ],
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "free_verifications_remaining": {
                      "type": "integer"
                    },
                    "trial_ends_at": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "trial_active": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "code",
                    "message",
                    "free_verifications_remaining",
                    "trial_ends_at",
                    "trial_active"
                  ]
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listVerifications",
        "description": "Lists the workspace's verifications, newest first, each in the shape Get verification status returns.\nFilter by `status` and `external_id`; page with `limit` and the `next_cursor` of the previous\npage, which is null on the last page.",
        "summary": "List verifications",
        "tags": [
          "Verification"
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Only verifications in one of these statuses, comma-separated: `created`, `started`, `submitted`, `resubmission_requested`, `approved`, `declined`, `abandoned`, `expired`, `review`. A verification waiting for a document is stored as `started` and listed as `documents_required`.",
            "schema": {
              "type": "string"
            },
            "example": "approved,declined"
          },
          {
            "name": "external_id",
            "in": "query",
            "description": "Only verifications created with this external_id (exact, case-sensitive match).",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many verifications to return, 1 to 100.",
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The `next_cursor` of the previous page. Send the same filters with it.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of verifications, newest first. `next_cursor` is null on the last page.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "data": [
                        {
                          "id": "550e8400-e29b-41d4-a716-446655440000",
                          "external_id": "user-123",
                          "external_metadata": {
                            "plan": "premium"
                          },
                          "redirect_url": "https://example.com/callback",
                          "status": "approved",
                          "reason": null,
                          "duplicate_check": {
                            "checked": true,
                            "duplicate_count": 0,
                            "duplicates": []
                          },
                          "erasure": null,
                          "consent_accepted_at": "2026-03-19T12:01:00+00:00",
                          "created_at": "2026-03-19T12:00:00+00:00",
                          "updated_at": "2026-03-19T12:05:00+00:00"
                        }
                      ],
                      "next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wMy0xOSAxMjowMDowMCIsImlkIjoiNTUwZTg0MDAtZTI5Yi00MWQ0LWE3MTYtNDQ2NjU1NDQwMDAwIiwiX3BvaW50c1RvTmV4dEl0ZW1zIjp0cnVlfQ"
                    }
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "external_id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "external_metadata": {
                            "type": [
                              "object",
                              "null"
                            ],
                            "additionalProperties": {}
                          },
                          "redirect_url": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "status": {
                            "type": "string"
                          },
                          "reason": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "duplicate_check": {
                            "type": "object",
                            "properties": {
                              "checked": {
                                "type": "boolean"
                              },
                              "duplicate_count": {
                                "type": "integer"
                              },
                              "duplicates": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "verification_id": {
                                      "type": "string"
                                    },
                                    "external_id": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "similarity_score": {
                                      "type": "number"
                                    },
                                    "verified_at": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    }
                                  },
                                  "required": [
                                    "verification_id",
                                    "external_id",
                                    "similarity_score",
                                    "verified_at"
                                  ]
                                }
                              }
                            },
                            "required": [
                              "checked",
                              "duplicate_count",
                              "duplicates"
                            ]
                          },
                          "erasure": {
                            "type": [
                              "object",
                              "null"
                            ],
                            "properties": {
                              "erased_at": {
                                "type": "string"
                              },
                              "scope": {
                                "type": "string"
                              },
                              "reason": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "requested_via": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            },
                            "required": [
                              "erased_at",
                              "scope",
                              "reason",
                              "requested_via"
                            ]
                          },
                          "consent_accepted_at": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "created_at": {
                            "type": "string"
                          },
                          "updated_at": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "external_id",
                          "external_metadata",
                          "redirect_url",
                          "status",
                          "reason",
                          "duplicate_check",
                          "erasure",
                          "consent_accepted_at",
                          "created_at",
                          "updated_at"
                        ]
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "next_cursor"
                  ]
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationException"
          }
        }
      }
    },
    "/verifications/{verification}/consent": {
      "post": {
        "operationId": "acceptConsent",
        "description": "Records the end-user's acceptance of the data processing consent.\nMust be called before uploading media or submitting the verification.",
        "summary": "Accept consent",
        "tags": [
          "Verification"
        ],
        "parameters": [
          {
            "name": "verification",
            "in": "path",
            "required": true,
            "description": "The verification ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreVerificationConsentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Consent is recorded; media can now be uploaded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "consent_version_id": 1,
                      "consent_accepted_at": "2026-03-19T12:01:00+00:00"
                    }
                  ],
                  "properties": {
                    "consent_version_id": {
                      "type": "integer"
                    },
                    "consent_accepted_at": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "consent_version_id",
                    "consent_accepted_at"
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ModelNotFoundException"
          },
          "422": {
            "$ref": "#/components/responses/ValidationException"
          }
        }
      }
    },
    "/verifications/{verification}": {
      "get": {
        "operationId": "getVerification",
        "description": "Retrieves the current state of a verification session including its status and decision reason.",
        "summary": "Get verification status",
        "tags": [
          "Verification"
        ],
        "parameters": [
          {
            "name": "verification",
            "in": "path",
            "required": true,
            "description": "The verification ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verification session state.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "external_id": "user-123",
                      "external_metadata": {
                        "plan": "premium"
                      },
                      "redirect_url": "https://example.com/callback",
                      "status": "approved",
                      "reason": null,
                      "duplicate_check": {
                        "checked": true,
                        "duplicate_count": 2,
                        "duplicates": [
                          {
                            "verification_id": "771a9200-c3df-4e88-b201-112233445566",
                            "external_id": "user-456",
                            "similarity_score": 0.93,
                            "verified_at": "2026-03-11T09:22:41+00:00"
                          }
                        ]
                      },
                      "erasure": null,
                      "consent_accepted_at": "2026-03-19T12:01:00+00:00",
                      "created_at": "2026-03-19T12:00:00+00:00",
                      "updated_at": "2026-03-19T12:05:00+00:00"
                    }
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "external_id": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "external_metadata": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "additionalProperties": {}
                    },
                    "redirect_url": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "status": {
                      "type": "string"
                    },
                    "reason": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "duplicate_check": {
                      "type": "object",
                      "properties": {
                        "checked": {
                          "type": "boolean"
                        },
                        "duplicate_count": {
                          "type": "integer"
                        },
                        "duplicates": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "verification_id": {
                                "type": "string"
                              },
                              "external_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "similarity_score": {
                                "type": "number"
                              },
                              "verified_at": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            },
                            "required": [
                              "verification_id",
                              "external_id",
                              "similarity_score",
                              "verified_at"
                            ]
                          }
                        }
                      },
                      "required": [
                        "checked",
                        "duplicate_count",
                        "duplicates"
                      ]
                    },
                    "erasure": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "erased_at": {
                          "type": "string"
                        },
                        "scope": {
                          "type": "string"
                        },
                        "reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "requested_via": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "erased_at",
                        "scope",
                        "reason",
                        "requested_via"
                      ]
                    },
                    "consent_accepted_at": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "created_at": {
                      "type": "string"
                    },
                    "updated_at": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "external_id",
                    "external_metadata",
                    "redirect_url",
                    "status",
                    "reason",
                    "duplicate_check",
                    "erasure",
                    "consent_accepted_at",
                    "created_at",
                    "updated_at"
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ModelNotFoundException"
          },
          "422": {
            "$ref": "#/components/responses/ValidationException"
          }
        }
      }
    },
    "/verifications/{verification}/estimation": {
      "get": {
        "operationId": "getVerificationEstimation",
        "description": "Retrieves sanitized age-threshold and gender estimates for age estimation workspaces.\nThe gender value uses 0 for female and 1 for male.",
        "summary": "Get verification age estimation result",
        "tags": [
          "Verification"
        ],
        "parameters": [
          {
            "name": "verification",
            "in": "path",
            "required": true,
            "description": "The verification ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verification age estimation result. Gender value mapping: 0 = female, 1 = male.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "verification_id": "019de7a0-c11b-7373-9289-b0aa1ba08d13",
                      "attempt_id": "019de7a2-0305-7269-9cff-1715c71b19ae",
                      "age_threshold": {
                        "minimum": 18,
                        "passed": true,
                        "confidence": 0.98
                      },
                      "gender": {
                        "value": 0,
                        "confidence": 0.93
                      }
                    }
                  ],
                  "properties": {
                    "verification_id": {
                      "type": "string"
                    },
                    "attempt_id": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "age_threshold": {
                      "type": "object",
                      "properties": {
                        "minimum": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "passed": {
                          "type": [
                            "boolean",
                            "null"
                          ]
                        },
                        "confidence": {
                          "type": [
                            "number",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "minimum",
                        "passed",
                        "confidence"
                      ]
                    },
                    "gender": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "value": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "confidence": {
                          "type": [
                            "number",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "value",
                        "confidence"
                      ]
                    }
                  },
                  "required": [
                    "verification_id",
                    "attempt_id",
                    "age_threshold",
                    "gender"
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ModelNotFoundException"
          },
          "422": {
            "$ref": "#/components/responses/ValidationException"
          }
        }
      }
    },
    "/verifications/{verification}/document": {
      "get": {
        "operationId": "getVerificationDocument",
        "description": "Returns what was read from the identity document of the most relevant attempt,\nnormalised: dates as YYYY-MM-DD, countries as ISO 3166-1 alpha-2 (XK for Kosovo), and null for\nanything the document did not yield or does not print. On identity (KYC)\nworkspaces `fields` carries eleven keys; on age verification workspaces only\n`first_name`, `last_name`, `date_of_birth` and `document_number` are returned.\nEach media item carries a `url` for the download endpoint, or null when the\nmedia has been purged or has passed its retention window.",
        "summary": "Get verification document result",
        "tags": [
          "Verification"
        ],
        "parameters": [
          {
            "name": "verification",
            "in": "path",
            "required": true,
            "description": "The verification ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verification document result. `type` and `gender` are open enums: handle values not listed here. `gender` is F, M or X, where X means the document states that the sex is unspecified. `other` in `type` is a readable document that is not an identity card, passport, driving licence or residence permit, such as a health insurance card. Countries are ISO 3166-1 alpha-2 (XK for Kosovo). A document that prints only the month or year of expiry reports the last day of that period; a partially printed birth or issue date is null. On a Mexican voter card or driving licence created before 20 August 2026 17:00 UTC, `gender` is null. The German identity card prints no sex, so `gender` is null there. A permanent document (for example `PERMANENTE` or `INDEFINIDA`) reads as a null `expiry_date`. `type` `id` is the same value the upload endpoint takes. `issuing_country` and `nationality` can differ, for example on a residence permit. `issuing_subdivision` is the state or province that issued the document, as a bare code beside `issuing_country` (for example `FL` with `US`), or null; today it is filled for US driving licences and ID cards. `place_of_birth` is the printed text, not normalised. `address` is the printed text as read, not parsed and not normalised, and may contain line breaks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "document": {
                        "type": "passport",
                        "issuing_country": "DE",
                        "issuing_subdivision": null,
                        "fields": {
                          "first_name": "JANE",
                          "middle_name": null,
                          "last_name": "DOE",
                          "date_of_birth": "1990-04-12",
                          "gender": "F",
                          "nationality": "DE",
                          "place_of_birth": "BERLIN",
                          "address": null,
                          "document_number": "C01X00T47",
                          "issue_date": "2020-04-14",
                          "expiry_date": "2030-04-13"
                        }
                      },
                      "media": [
                        {
                          "id": "019de7a0-c11b-7373-9289-b0aa1ba08d13",
                          "type": "selfie",
                          "url": "https://api.proofage.net/v1/verifications/019de7a0-c11b-7373-9289-b0aa1ba08d13/media/019de7a0-c11b-7373-9289-b0aa1ba08d13"
                        },
                        {
                          "id": "019de7a0-c129-7259-8a08-bc10390ecbdb",
                          "type": "document_front",
                          "url": "https://api.proofage.net/v1/verifications/019de7a0-c11b-7373-9289-b0aa1ba08d13/media/019de7a0-c129-7259-8a08-bc10390ecbdb"
                        }
                      ],
                      "meta": {
                        "attempt_id": "019de7a2-0305-7269-9cff-1715c71b19ae"
                      }
                    },
                    {
                      "document": {
                        "type": "id",
                        "issuing_country": "FR",
                        "issuing_subdivision": null,
                        "fields": {
                          "first_name": "JEAN",
                          "last_name": "MARTIN",
                          "date_of_birth": "1988-11-02",
                          "document_number": "X4RTBPFW4"
                        }
                      },
                      "media": [
                        {
                          "id": "019de7a0-c129-7259-8a08-bc10390ecbdb",
                          "type": "document_front",
                          "url": "https://api.proofage.net/v1/verifications/019de7a0-c11b-7373-9289-b0aa1ba08d13/media/019de7a0-c129-7259-8a08-bc10390ecbdb"
                        }
                      ],
                      "meta": {
                        "attempt_id": "019de7a2-0305-7269-9cff-1715c71b19ae"
                      }
                    }
                  ],
                  "properties": {
                    "document": {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "enum": [
                            "passport",
                            "id",
                            "driver_license",
                            "residence_permit",
                            "other",
                            null
                          ]
                        },
                        "issuing_country": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "issuing_subdivision": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "fields": {
                          "type": "object",
                          "properties": {
                            "first_name": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "middle_name": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "last_name": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "date_of_birth": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "date"
                            },
                            "gender": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "enum": [
                                "F",
                                "M",
                                "X",
                                null
                              ]
                            },
                            "nationality": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "place_of_birth": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "address": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "document_number": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "issue_date": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "date"
                            },
                            "expiry_date": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "date"
                            }
                          },
                          "required": [
                            "first_name",
                            "last_name",
                            "date_of_birth",
                            "document_number"
                          ]
                        }
                      },
                      "required": [
                        "type",
                        "issuing_country",
                        "issuing_subdivision",
                        "fields"
                      ]
                    },
                    "media": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "url": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "type",
                          "url"
                        ]
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "attempt_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "attempt_id"
                      ]
                    }
                  },
                  "required": [
                    "document",
                    "media",
                    "meta"
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ModelNotFoundException"
          },
          "422": {
            "$ref": "#/components/responses/ValidationException"
          }
        }
      }
    },
    "/verifications/{verification}/submit": {
      "post": {
        "operationId": "submitVerification",
        "description": "Submits the verification for automated processing once the current attempt's media is uploaded.\nAn upload moves the verification to `started`. A facial age estimation that asked for a document\nis submitted the same way, after the document is uploaded.\n\nAnswers `200` with an empty body; the decision arrives by webhook.",
        "summary": "Submit verification for processing",
        "tags": [
          "Verification"
        ],
        "parameters": [
          {
            "name": "verification",
            "in": "path",
            "required": true,
            "description": "The verification ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The verification was submitted. The body is empty.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "examples": [
                    ""
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ModelNotFoundException"
          },
          "422": {
            "description": "The verification is not waiting to be submitted (`INVALID_STATUS`), or required media is missing (`MISSING_REQUIRED_MEDIA`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "error": {
                        "code": "MISSING_REQUIRED_MEDIA",
                        "message": "All required media must be uploaded before submitting."
                      }
                    }
                  ],
                  "properties": {
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string"
                        },
                        "message": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "code",
                        "message"
                      ]
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/verifications/{verification}/blocked-face": {
      "post": {
        "operationId": "blockVerificationFace",
        "description": "Adds the face from this verification's selfie to your blocklist, shared by all your workspaces.\nLater verifications with a matching face are declined.\nOptional `reason` text is stored with the block and truncated to 1000 characters.\n\nOptional `reason_code` classifies the block, and is what reporting counts: one of\n`presentation_attack`, `fraudulent_document`, `scam_or_abuse`, `underage`, `other`.\nSend it whenever a person made the decision \u2014 the admin consoles require it, so a\nblock that arrives without one cannot be told apart from an automated one.",
        "summary": "Block a verification face",
        "tags": [
          "Verification"
        ],
        "parameters": [
          {
            "name": "verification",
            "in": "path",
            "required": true,
            "description": "The verification ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BlockVerificationFaceRequest"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "No content"
          },
          "404": {
            "$ref": "#/components/responses/ModelNotFoundException"
          },
          "422": {
            "$ref": "#/components/responses/ValidationException"
          }
        }
      }
    },
    "/verifications/{verification}/test-outcome": {
      "post": {
        "operationId": "setTestVerificationOutcome",
        "description": "Test workspaces only: finishes a verification with the given status without a person going\nthrough the widget, so an integration's handling of each outcome can be tested end to end.\nA verification nobody has opened is moved through `started` and `submitted` first, as a\nperson's submission would move it, so the outcome is the only decision webhooks are sent for.\nWorks from `created`, `started`, `submitted`, `review` and `resubmission_requested`.\n\nAnswers with the verification, as Get verification status does.",
        "summary": "Set a test outcome",
        "tags": [
          "Verification"
        ],
        "parameters": [
          {
            "name": "verification",
            "in": "path",
            "required": true,
            "description": "The verification ID",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetTestVerificationOutcomeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The verification with its new status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "id": "550e8400-e29b-41d4-a716-446655440000",
                      "external_id": "user-123",
                      "external_metadata": null,
                      "redirect_url": "https://example.com/callback",
                      "status": "approved",
                      "reason": null,
                      "duplicate_check": {
                        "checked": false,
                        "duplicate_count": 0,
                        "duplicates": []
                      },
                      "erasure": null,
                      "consent_accepted_at": null,
                      "created_at": "2026-03-19T12:00:00+00:00",
                      "updated_at": "2026-03-19T12:00:05+00:00"
                    }
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "external_id": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "external_metadata": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "additionalProperties": {}
                    },
                    "redirect_url": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "status": {
                      "type": "string"
                    },
                    "reason": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "duplicate_check": {
                      "type": "object",
                      "properties": {
                        "checked": {
                          "type": "boolean"
                        },
                        "duplicate_count": {
                          "type": "integer"
                        },
                        "duplicates": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "verification_id": {
                                "type": "string"
                              },
                              "external_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "similarity_score": {
                                "type": "number"
                              },
                              "verified_at": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            },
                            "required": [
                              "verification_id",
                              "external_id",
                              "similarity_score",
                              "verified_at"
                            ]
                          }
                        }
                      },
                      "required": [
                        "checked",
                        "duplicate_count",
                        "duplicates"
                      ]
                    },
                    "erasure": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "erased_at": {
                          "type": "string"
                        },
                        "scope": {
                          "type": "string"
                        },
                        "reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "requested_via": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "erased_at",
                        "scope",
                        "reason",
                        "requested_via"
                      ]
                    },
                    "consent_accepted_at": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "created_at": {
                      "type": "string"
                    },
                    "updated_at": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "external_id",
                    "external_metadata",
                    "redirect_url",
                    "status",
                    "reason",
                    "duplicate_check",
                    "erasure",
                    "consent_accepted_at",
                    "created_at",
                    "updated_at"
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ModelNotFoundException"
          },
          "422": {
            "description": "The verification is already final (`INVALID_STATUS`), or a field is invalid (`message` and `errors` by field).",
            "content": {
              "application/json": {
                "schema": {
                  "examples": [
                    {
                      "error": {
                        "code": "INVALID_STATUS",
                        "message": "The verification is already approved, a final status."
                      }
                    }
                  ],
                  "anyOf": [
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "object",
                          "properties": {
                            "code": {
                              "type": "string"
                            },
                            "message": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "code",
                            "message"
                          ]
                        }
                      },
                      "required": [
                        "error"
                      ]
                    },
                    {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string"
                        },
                        "errors": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      },
                      "required": [
                        "message",
                        "errors"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The workspace is a live one (`TEST_WORKSPACE_ONLY`), or the verification belongs to another workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "examples": [
                    {
                      "error": {
                        "code": "TEST_WORKSPACE_ONLY",
                        "message": "The outcome can only be set in a test workspace."
                      }
                    }
                  ],
                  "anyOf": [
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "object",
                          "properties": {
                            "code": {
                              "type": "string"
                            },
                            "message": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "code",
                            "message"
                          ]
                        }
                      },
                      "required": [
                        "error"
                      ]
                    },
                    {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "message"
                      ]
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/webhook-subscriptions": {
      "post": {
        "operationId": "createWebhookSubscription",
        "description": "Subscribes a URL to decision webhooks, in addition to the workspace webhook URL set in the console.\nBuilt for REST hooks such as Zapier: subscribe when an automation is turned on, delete the\nsubscription when it is turned off. A workspace can have up to 50.\n\nEach delivery has the workspace webhook's body and headers, signed with the secret key that\nsigned this request while that key exists, and with the active secret key after it is deleted.\nUnless `include_document_data` is true, the body leaves out `document`, `fingerprint_signals` and\n`manual_moderation.performed_by`. A delivery answered with `410 Gone` deletes the subscription.",
        "summary": "Create a webhook subscription",
        "tags": [
          "WebhookSubscription"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreWebhookSubscriptionRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The subscription. `statuses` is null when it receives every decision status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "id": "0199c4b2-7d1e-7a3f-9c0e-5b6a7c8d9e0f",
                      "url": "https://hooks.zapier.com/hooks/standard/12345678/abcdef/",
                      "statuses": [
                        "approved",
                        "declined"
                      ],
                      "include_document_data": false,
                      "created_at": "2026-10-08T12:00:00+00:00"
                    }
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "statuses": {
                      "type": [
                        "array",
                        "null"
                      ],
                      "items": {
                        "type": "string"
                      }
                    },
                    "include_document_data": {
                      "type": "boolean"
                    },
                    "created_at": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "url",
                    "statuses",
                    "include_document_data",
                    "created_at"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "The workspace already has 50 webhook subscriptions (`WEBHOOK_SUBSCRIPTION_LIMIT`), or a field is invalid (`message` and `errors` by field).",
            "content": {
              "application/json": {
                "schema": {
                  "examples": [
                    {
                      "error": {
                        "code": "WEBHOOK_SUBSCRIPTION_LIMIT",
                        "message": "A workspace can have at most 50 webhook subscriptions. Delete one first."
                      }
                    }
                  ],
                  "anyOf": [
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "object",
                          "properties": {
                            "code": {
                              "type": "string"
                            },
                            "message": {
                              "type": "string"
                            }
                          },
                          "required": [
                            "code",
                            "message"
                          ]
                        }
                      },
                      "required": [
                        "error"
                      ]
                    },
                    {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string"
                        },
                        "errors": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      },
                      "required": [
                        "message",
                        "errors"
                      ]
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listWebhookSubscriptions",
        "description": "Lists the workspace's webhook subscriptions, newest first.",
        "summary": "List webhook subscriptions",
        "tags": [
          "WebhookSubscription"
        ],
        "responses": {
          "200": {
            "description": "The workspace's webhook subscriptions, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "data": [
                        {
                          "id": "0199c4b2-7d1e-7a3f-9c0e-5b6a7c8d9e0f",
                          "url": "https://hooks.zapier.com/hooks/standard/12345678/abcdef/",
                          "statuses": null,
                          "include_document_data": false,
                          "created_at": "2026-10-08T12:00:00+00:00"
                        }
                      ]
                    }
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string"
                          },
                          "statuses": {
                            "type": [
                              "array",
                              "null"
                            ],
                            "items": {
                              "type": "string"
                            }
                          },
                          "include_document_data": {
                            "type": "boolean"
                          },
                          "created_at": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "url",
                          "statuses",
                          "include_document_data",
                          "created_at"
                        ]
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/webhook-subscriptions/{subscription}": {
      "delete": {
        "operationId": "deleteWebhookSubscription",
        "description": "Stops the deliveries to the subscription. Deliveries already queued are not sent.",
        "summary": "Delete a webhook subscription",
        "tags": [
          "WebhookSubscription"
        ],
        "parameters": [
          {
            "name": "subscription",
            "in": "path",
            "required": true,
            "description": "The webhook subscription ID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The subscription is deleted. The body is empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No subscription with this id in the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "message": "Resource not found"
                    }
                  ],
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "message"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/workspace": {
      "get": {
        "operationId": "getWorkspace",
        "description": "Returns the workspace settings including flow type, verification mode, and age thresholds.",
        "summary": "Get workspace configuration",
        "tags": [
          "Workspace"
        ],
        "responses": {
          "200": {
            "description": "`WorkspaceResource`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceResource"
                }
              }
            }
          }
        }
      }
    },
    "/consent": {
      "get": {
        "operationId": "getConsent",
        "description": "Returns the currently active consent version that must be presented to and accepted by the end-user.",
        "summary": "Get active consent",
        "tags": [
          "Workspace"
        ],
        "responses": {
          "200": {
            "description": "The active consent version. Show the text at url, then send id and text_sha256 back when accepting.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "examples": [
                    {
                      "id": 1,
                      "version": 2,
                      "text_sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                      "url": "https://app.proofage.net/consent"
                    }
                  ],
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "version": {
                      "type": "integer"
                    },
                    "text_sha256": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "version",
                    "text_sha256",
                    "url"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "description": "Your workspace public key (pk_test_\u2026 or pk_live_\u2026).",
        "in": "header",
        "name": "X-API-Key"
      },
      "hmacSignature": {
        "type": "apiKey",
        "description": "HMAC-SHA256 of the request, hex-encoded, keyed with the workspace secret key. See API authentication.",
        "in": "header",
        "name": "X-HMAC-Signature"
      }
    },
    "schemas": {
      "BlockVerificationFaceRequest": {
        "type": "object",
        "properties": {
          "reason_code": {
            "$ref": "#/components/schemas/BlockedFaceReasonCode"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Free text stored with the block; longer text is cut at 1000 characters."
          }
        },
        "title": "BlockVerificationFaceRequest"
      },
      "BlockedFaceReasonCode": {
        "type": "string",
        "description": "Why a person put this face on the blocklist. The admin consoles require one; the public API accepts it optionally, and propagated device matches carry none.\n",
        "enum": [
          "presentation_attack",
          "fraudulent_document",
          "scam_or_abuse",
          "underage",
          "other"
        ],
        "title": "BlockedFaceReasonCode"
      },
      "SetTestVerificationOutcomeRequest": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "The outcome to set: `approved`, `declined`, `review` or `resubmission_requested`.",
            "enum": [
              "approved",
              "declined",
              "review",
              "resubmission_requested"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "A note kept with a `resubmission_requested` outcome in the verification's history. It is not the decision `reason` code.",
            "maxLength": 1000
          }
        },
        "required": [
          "status"
        ],
        "title": "SetTestVerificationOutcomeRequest"
      },
      "StoreVerificationConsentRequest": {
        "type": "object",
        "properties": {
          "consent_version_id": {
            "type": "integer"
          },
          "text_sha256": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$",
            "minLength": 64,
            "maxLength": 64
          }
        },
        "required": [
          "consent_version_id",
          "text_sha256"
        ],
        "title": "StoreVerificationConsentRequest"
      },
      "StoreVerificationRequest": {
        "type": "object",
        "properties": {
          "callback_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Where the person's browser is sent after the final screen, an http or https URL. Ignored unless the request is HMAC-signed.",
            "maxLength": 2048
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your identifier for the person. Ignored unless the request is HMAC-signed: the public key alone cannot tie a session to your user.",
            "maxLength": 255
          },
          "external_metadata": {
            "type": [
              "object",
              "null"
            ],
            "description": "Free-form data returned in the API and webhooks. Accepted without a signature, so treat it as client-supplied.",
            "additionalProperties": {}
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "description": "Free-form data kept on the session and not returned to integrations.",
            "additionalProperties": {}
          }
        },
        "title": "StoreVerificationRequest"
      },
      "StoreWebhookSubscriptionRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Where ProofAge POSTs the decision webhooks, signed like the workspace webhook. It must be a public URL: private, local and cloud-metadata addresses are refused.",
            "maxLength": 2048
          },
          "include_document_data": {
            "type": "boolean",
            "description": "Include the document read from the identity document (names, date of birth, document number), the fingerprint signals (IP address, timezones) and the name and email of the operator who moderated. Off by default, so personal data stays out of the subscriber's logs.",
            "default": false
          },
          "statuses": {
            "type": [
              "array",
              "null"
            ],
            "description": "Only send these statuses: `approved`, `declined`, `resubmission_requested`, `review`, `abandoned`, `expired`. Omit it, or send null, for all of them.",
            "items": {
              "type": "string",
              "enum": [
                "approved",
                "declined",
                "resubmission_requested",
                "abandoned",
                "expired",
                "review"
              ]
            }
          }
        },
        "required": [
          "url"
        ],
        "title": "StoreWebhookSubscriptionRequest"
      },
      "UploadMediaRequest": {
        "type": "object",
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "contentMediaType": "application/octet-stream"
          },
          "type": {
            "type": "string",
            "description": "What the file is: the person's selfie, or one side of their document.",
            "enum": [
              "selfie",
              "document"
            ]
          },
          "side": {
            "type": "string",
            "description": "Which side of the document. Required when `type` is `document`.",
            "enum": [
              "front",
              "back"
            ]
          },
          "document": {
            "type": "string",
            "description": "The kind of document. Required when `type` is `document`.",
            "enum": [
              "id",
              "driver_license",
              "passport",
              "residence_permit"
            ]
          }
        },
        "required": [
          "file",
          "type"
        ],
        "title": "UploadMediaRequest"
      },
      "VerificationDocumentResource": {
        "type": "object",
        "properties": {
          "document": {
            "type": "object",
            "properties": {
              "type": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "issuing_country": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "issuing_subdivision": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "fields": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "first_name": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "middle_name": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "last_name": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "date_of_birth": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "gender": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "nationality": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "place_of_birth": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "address": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "document_number": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "issue_date": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "expiry_date": {
                        "type": [
                          "string",
                          "null"
                        ]
                      }
                    },
                    "required": [
                      "first_name",
                      "middle_name",
                      "last_name",
                      "date_of_birth",
                      "gender",
                      "nationality",
                      "place_of_birth",
                      "address",
                      "document_number",
                      "issue_date",
                      "expiry_date"
                    ]
                  },
                  {
                    "type": "array",
                    "items": {}
                  }
                ]
              }
            },
            "required": [
              "type",
              "issuing_country",
              "issuing_subdivision",
              "fields"
            ]
          },
          "media": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                },
                "url": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "id",
                "type",
                "url"
              ]
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "attempt_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "attempt_id"
            ]
          }
        },
        "required": [
          "document",
          "media",
          "meta"
        ],
        "title": "VerificationDocumentResource"
      },
      "VerificationEstimationResource": {
        "type": "object",
        "properties": {
          "verification_id": {
            "type": "string"
          },
          "attempt_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "age_threshold": {
            "type": "object",
            "properties": {
              "minimum": {
                "type": "integer"
              },
              "passed": {
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "confidence": {
                "type": [
                  "number",
                  "null"
                ]
              }
            },
            "required": [
              "minimum",
              "passed",
              "confidence"
            ]
          },
          "gender": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "value": {
                "type": [
                  "integer",
                  "null"
                ],
                "enum": [
                  0,
                  1,
                  null
                ]
              },
              "confidence": {
                "type": [
                  "number",
                  "null"
                ]
              }
            },
            "required": [
              "value",
              "confidence"
            ]
          }
        },
        "required": [
          "verification_id",
          "attempt_id",
          "age_threshold",
          "gender"
        ],
        "title": "VerificationEstimationResource"
      },
      "WebhookSubscriptionResource": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "statuses": {
            "type": [
              "array",
              "null"
            ],
            "items": {}
          },
          "include_document_data": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "url",
          "statuses",
          "include_document_data",
          "created_at"
        ],
        "title": "WebhookSubscriptionResource"
      },
      "WorkspaceResource": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "flow_type": {
            "type": "string"
          },
          "mode": {
            "type": "string"
          },
          "age_mode": {
            "type": [
              "string",
              "null"
            ]
          },
          "age_threshold": {
            "type": [
              "integer",
              "null"
            ]
          },
          "verification_type": {
            "type": "string"
          },
          "redirect_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "webhook_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "allow_expired_documents": {
            "type": "boolean"
          },
          "allow_duplicate_accounts": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "name",
          "flow_type",
          "mode",
          "age_mode",
          "age_threshold",
          "verification_type",
          "redirect_url",
          "webhook_url",
          "allow_expired_documents",
          "allow_duplicate_accounts"
        ],
        "title": "WorkspaceResource"
      }
    },
    "responses": {
      "ValidationException": {
        "description": "Validation error",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string",
                  "description": "Errors overview."
                },
                "errors": {
                  "type": "object",
                  "description": "A detailed description of each field that failed validation.",
                  "additionalProperties": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "required": [
                "message",
                "errors"
              ]
            }
          }
        }
      },
      "ModelNotFoundException": {
        "description": "Not found",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string",
                  "description": "Error overview."
                }
              },
              "required": [
                "message"
              ]
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "verificationDecision": {
      "post": {
        "summary": "Verification status changed, or document data corrected",
        "description": "ProofAge sends this request to the workspace's webhook URL each time a verification moves to `approved`, `declined`, `resubmission_requested`, `review`, `abandoned` or `expired`. One verification can therefore produce several events. The same request, with `event` set to `data.updated`, is also sent when someone on your team corrects the document fields the reader got wrong, from the console or the MCP server; `status` is then the verification's current status, unchanged. Dispatch on `event` first. Each webhook subscription created with `POST /v1/webhook-subscriptions` also receives the `status.updated` events for the statuses it asked for, never `data.updated`; unless it was created with `include_document_data`, its body leaves out `document`, `fingerprint_signals` and `manual_moderation.performed_by`. Verify `X-HMAC-Signature` before trusting the body: it is the hex HMAC-SHA256 of `{X-Timestamp}.{raw body}`, keyed with the workspace's active secret key, or for a subscription with the secret key that created it. Respond with any 2xx. A 408, 429 or 5xx response, or no response within 30 seconds, is retried after 5, 15 and 60 minutes. Redirects are not followed. A subscription whose URL answers 410 is deleted. See [Webhooks](/integration/webhooks).",
        "parameters": [
          {
            "name": "X-HMAC-Signature",
            "in": "header",
            "required": true,
            "description": "Hex HMAC-SHA256 of `{X-Timestamp}.{raw body}` with the workspace's active secret key; for a webhook subscription, with the secret key that created it (the active key once that one is deleted).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time, in seconds, when the request was signed.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "X-Auth-Client",
            "in": "header",
            "required": true,
            "description": "The workspace's public key (`pk_test_\u2026` or `pk_live_\u2026`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-ProofAge-Webhook-Delivery-Id",
            "in": "header",
            "required": true,
            "description": "ID of this delivery. A retry of the same delivery keeps it; use it to ignore duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "verification_id",
                  "status",
                  "external_id",
                  "external_metadata",
                  "reason",
                  "timestamp"
                ],
                "properties": {
                  "verification_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The verification this event is about."
                  },
                  "event": {
                    "type": "string",
                    "enum": [
                      "status.updated",
                      "data.updated"
                    ],
                    "description": "What happened. `status.updated`: the verification moved to `status`. `data.updated`: someone on your team corrected document fields; `status` is the current status, which a correction never changes, `document` carries the corrected values and `changed_fields` names what changed. A payload without `event`, such as a retry of a delivery created before this field existed, means `status.updated`. Webhook subscriptions receive `status.updated` only."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "approved",
                      "declined",
                      "resubmission_requested",
                      "review",
                      "abandoned",
                      "expired"
                    ],
                    "description": "The status the verification moved to. On a `data.updated` event, the current status, unchanged."
                  },
                  "external_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Your identifier for the person, as sent when the verification was created."
                  },
                  "external_metadata": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "description": "The metadata you attached when creating the verification, returned as sent.",
                    "additionalProperties": {}
                  },
                  "reason": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "The decision reason code when the status is `declined` or `resubmission_requested`; `null` otherwise. Also `null` on the `resubmission_requested` a facial age estimation sends when it asks the person for an ID (`GET` reports that verification as `documents_required`). When attempts run out, the `declined` event carries the last attempt's reason. See Decision reasons."
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the status changed, ISO 8601."
                  },
                  "document": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "enum": [
                          "passport",
                          "id",
                          "driver_license",
                          "residence_permit",
                          "other",
                          null
                        ]
                      },
                      "issuing_country": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "issuing_subdivision": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "The state or province that issued the document, as a bare code beside `issuing_country` (for example `FL` with `US`), or null. Today it is filled for US driving licences and ID cards. Present on every workspace and kept after erasure."
                      },
                      "fields": {
                        "type": "object",
                        "properties": {
                          "first_name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "middle_name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "last_name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "date_of_birth": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date"
                          },
                          "gender": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "enum": [
                              "F",
                              "M",
                              "X",
                              null
                            ],
                            "description": "`X` means the document states that the sex is unspecified."
                          },
                          "nationality": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Published only when the document itself states it."
                          },
                          "place_of_birth": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "address": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "KYC workspaces only. The printed text as read: not parsed, may contain line breaks."
                          },
                          "document_number": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "issue_date": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date"
                          },
                          "expiry_date": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date"
                          }
                        },
                        "required": [
                          "first_name",
                          "last_name",
                          "date_of_birth",
                          "document_number"
                        ]
                      }
                    },
                    "required": [
                      "type",
                      "issuing_country",
                      "issuing_subdivision",
                      "fields"
                    ],
                    "description": "The same object `GET /v1/verifications/{id}/document` returns, without `media` and `meta`, read from the attempt that decided the verification. Identity (KYC) workspaces receive eleven `fields`; age verification workspaces receive `first_name`, `last_name`, `date_of_birth` and `document_number` only, and the other seven keys are absent. `null` means the field was not read, is not printed on that document, or no document was read at all (a facial age estimation that passed without an ID, a test workspace, a wallet check); `document` itself is never null. Dates are `YYYY-MM-DD` and countries ISO 3166-1 alpha-2 (`XK` for Kosovo); `type` and `gender` are open enums. Fields your team corrected show the corrected value. A resend or a manual retry carries the document as it is now; an automatic retry carries the body as first sent. After the person's data is erased, only `type` and `issuing_country` are kept. Always present on the workspace webhook; left out of a webhook subscription's deliveries unless it was created with `include_document_data`."
                  },
                  "changed_fields": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Only on `data.updated`: the names of what this correction changed, for example `[\"date_of_birth\"]`. A name is a key of `document.fields`, or `type`, `issuing_country` or `issuing_subdivision`. Names only; the new values are in `document`."
                  },
                  "duplicate_detected": {
                    "type": "boolean",
                    "description": "Present and `true` only when the same face was found behind another verification in the workspace."
                  },
                  "duplicate_count": {
                    "type": "integer",
                    "description": "Present with `duplicate_detected`: how many earlier verifications matched."
                  },
                  "duplicate_of": {
                    "type": "object",
                    "description": "Present with `duplicate_detected`: the first matching verification.",
                    "properties": {
                      "verification_id": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "external_id": {
                        "type": [
                          "string",
                          "null"
                        ]
                      }
                    }
                  },
                  "fingerprint_signals": {
                    "type": "object",
                    "description": "Optional device and network signals collected during the session. Treat it as informational; its fields may change. Left out of a webhook subscription's deliveries unless it was created with `include_document_data`.",
                    "additionalProperties": {}
                  },
                  "manual_moderation": {
                    "type": "object",
                    "description": "Present when a person in the console approved or declined the verification by hand.",
                    "properties": {
                      "action": {
                        "type": "string",
                        "enum": [
                          "approve",
                          "decline"
                        ]
                      },
                      "reason": {
                        "type": "string",
                        "description": "The reason the moderator gave."
                      },
                      "source": {
                        "type": "string",
                        "enum": [
                          "tenant_admin",
                          "landlord_admin"
                        ],
                        "description": "`tenant_admin` is someone on your team; `landlord_admin` is ProofAge staff."
                      },
                      "performed_by": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "name": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string"
                          },
                          "role": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        },
                        "description": "Who moderated. Left out of a webhook subscription's deliveries unless it was created with `include_document_data`."
                      },
                      "source_status": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "On an approval: the status the verification had before it."
                      },
                      "source_reason": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "On an approval: the reason code it had before it."
                      }
                    }
                  }
                }
              },
              "example": {
                "verification_id": "550e8400-e29b-41d4-a716-446655440000",
                "status": "declined",
                "external_id": "user_12345",
                "external_metadata": {
                  "plan": "premium"
                },
                "reason": "document.face.mismatch",
                "timestamp": "2026-09-29T12:05:00+00:00",
                "document": {
                  "type": "passport",
                  "issuing_country": "DE",
                  "issuing_subdivision": null,
                  "fields": {
                    "first_name": "JANE",
                    "middle_name": null,
                    "last_name": "DOE",
                    "date_of_birth": "1990-04-12",
                    "gender": "F",
                    "nationality": "DE",
                    "place_of_birth": "BERLIN",
                    "address": null,
                    "document_number": "C01X00T47",
                    "issue_date": "2020-04-14",
                    "expiry_date": "2030-04-13"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery."
          }
        },
        "security": []
      }
    }
  }
}
