{"openapi":"3.1.0","info":{"title":"VerifyMX API","description":"Identity verification API for the Mexican market. Drop-in MetaMap-compatible REST interface with native support for INE, CURP, RFC and passport.","version":"1.0.0","contact":{"name":"VerifyMX","url":"https://verifymx.xyz","email":"support@verifymx.xyz"},"license":{"name":"Proprietary","url":"https://verifymx.xyz/legal"}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"apiKey":{"type":"apiKey","in":"header","name":"X-Api-Key"}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable machine-readable code. SDKs branch on this. Taxonomy: VALIDATION_ERROR, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, CONFLICT, RATE_LIMITED, INTERNAL_ERROR, ...","example":"VALIDATION_ERROR"},"message":{"type":"string","description":"Human-readable explanation. Safe to surface to end users.","example":"curp must be 18 characters"},"details":{"type":"array","items":{"type":"string"},"description":"Optional per-field validation breakdown."},"requestId":{"type":"string","description":"Echo of the x-request-id header for support tickets."},"retryAfter":{"type":"integer","description":"Seconds to wait before retrying (set on 429)."}}}}},"def-0":{"type":"object","additionalProperties":true,"properties":{"error":{"type":"object","additionalProperties":true,"properties":{"code":{"type":"string","description":"Stable machine-readable code. SDKs branch on this. Taxonomy: VALIDATION_ERROR, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, CONFLICT, RATE_LIMITED, INTERNAL_ERROR."},"message":{"type":"string"},"details":{"type":"array"},"requestId":{"type":"string"},"retryAfter":{"type":"integer"}}}},"title":"errorSchema"}}},"paths":{"/health":{"get":{"summary":"Health check","tags":["system"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"uptime":{"type":"number"},"timestamp":{"type":"string"},"redis":{"type":"string"}}}}}}}}},"/health/services":{"get":{"summary":"Rollup health probe across ml-ocr, ml-face, gov-bridge, synthetic-ine","tags":["system"],"responses":{"200":{"description":"Default Response"}}}},"/v1/auth/token":{"post":{"operationId":"exchangeToken","summary":"Exchange client credentials for a bearer token","tags":["auth"],"description":"OAuth 2.0 client_credentials grant. Returns a short-lived JWT used as `Authorization: Bearer <token>` on every other endpoint. Cache and refresh before expiry.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["client_id","client_secret","grant_type"],"properties":{"grant_type":{"type":"string","enum":["client_credentials"]},"client_id":{"type":"string","minLength":1},"client_secret":{"type":"string","minLength":1}},"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string","description":"Bearer token. Pass as `Authorization: Bearer <token>` on subsequent calls."},"token_type":{"type":"string","description":"Always \"bearer\"."},"expires_in":{"type":"integer","description":"Seconds until token expiry (typically 3600)."}},"required":["access_token","token_type","expires_in"]}}}},"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/auth/login":{"post":{"operationId":"loginWithPassword","summary":"Email + password login (dashboard use)","tags":["auth"],"description":"Issues a dashboard JWT (userId-bearing). Customers integrating server-to-server should use POST /auth/token (client_credentials), not this endpoint.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string"},"password":{"type":"string","minLength":1,"maxLength":256}},"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string"},"expires_in":{"type":"integer"},"user":{"type":"object","additionalProperties":true},"org":{"type":"object","additionalProperties":true}}}}}},"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/auth/forgot-password":{"post":{"operationId":"forgotPassword","summary":"Request a password reset email","tags":["auth"],"description":"Always returns 202 regardless of whether the email matches an active user, to prevent account enumeration. If the email is real, a single-use reset link valid for 60 minutes is delivered.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}},"additionalProperties":false}}}},"responses":{"202":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/auth/reset-password":{"post":{"operationId":"resetPassword","summary":"Consume a reset token and set a new password","tags":["auth"],"description":"Validates a token from POST /auth/forgot-password and writes the new password hash. Tokens are single-use and expire 60 minutes after issue.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token","password"],"properties":{"token":{"type":"string","minLength":32,"maxLength":128},"password":{"type":"string","minLength":12,"maxLength":128}},"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/auth/register":{"post":{"operationId":"registerOrganization","summary":"Self-serve org registration","tags":["auth"],"description":"Creates an organization, a primary user, and returns the first API key. The API key is shown ONCE — store it securely. For programmatic use, prefer dashboard provisioning.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["orgName","email","password"],"properties":{"orgName":{"type":"string","minLength":2,"maxLength":100},"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":12,"maxLength":128}},"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"org":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}}},"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string"}}},"apiKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"key":{"type":"string"},"prefix":{"type":"string"},"createdAt":{"type":"string"}}}}}}}},"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/verifications":{"post":{"operationId":"createVerification","summary":"Create a verification","tags":["verifications"],"description":"Starts a new verification session against a workflow. Returns the verification id immediately; document uploads, gov-DB checks and the final decision happen asynchronously. Subscribe to webhooks or poll GET /verifications/:id.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["workflowId"],"properties":{"workflowId":{"type":"string","minLength":1},"metadata":{"type":"object","additionalProperties":true},"callbackUrl":{"type":"string","format":"uri","maxLength":2048},"externalId":{"type":"string","maxLength":255}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}},"get":{"operationId":"listVerifications","summary":"List verifications","tags":["verifications"],"description":"Returns the most recent verifications for the calling organization, paginated by offset+limit. Use `status` to filter by lifecycle stage and `search` for a free-text query over names, emails, and external ids.","parameters":[{"schema":{"type":"string","enum":["PENDING","IN_PROGRESS","APPROVED","REJECTED","EXPIRED","MANUAL_REVIEW"]},"in":"query","name":"status","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"limit","required":false},{"schema":{"type":"integer","minimum":0,"default":0},"in":"query","name":"offset","required":false},{"schema":{"type":"string","maxLength":255},"in":"query","name":"search","required":false}],"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/verifications/{id}":{"get":{"operationId":"getVerification","summary":"Get a verification by id","tags":["verifications"],"description":"Returns the verification plus every step, every uploaded document, and per-field extraction decisions. This is the polling target if you are not using webhooks.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"Verification id returned by createVerification."}],"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/verifications/{id}/status":{"patch":{"operationId":"updateVerificationStatus","summary":"Manually override verification status (reviewer+ only)","tags":["verifications"],"description":"Force the verification into APPROVED / REJECTED / MANUAL_REVIEW regardless of automated signals. Audit-logged and webhook-dispatched. Requires REVIEWER role or higher on the calling user / API key.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["APPROVED","REJECTED","MANUAL_REVIEW"]},"notes":{"type":"string","maxLength":2000}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/verifications/{id}/inputs":{"post":{"operationId":"uploadVerificationInputs","summary":"Upload document images and selfies to a verification","tags":["verifications"],"description":"Multipart upload. Field names map to document role: `front` / `back` / `selfie` / `passport`. JPEG / PNG / WebP / PDF accepted, 10 MB max per file. Each file is buffered, persisted, and an OCR job is enqueued.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/webhooks":{"get":{"operationId":"listWebhooks","summary":"List webhook endpoints","tags":["webhooks"],"description":"Returns every webhook endpoint registered for the organization. Signing secrets are NEVER returned in this response — they are only shown once at creation.","security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}},"post":{"operationId":"createWebhook","summary":"Register a webhook endpoint","tags":["webhooks"],"description":"Registers a URL to receive webhook events. If `secret` is omitted, the server generates one — it is returned ONCE in the response and never again. Use it to verify the `x-verifymx-signature` HMAC on incoming deliveries. OWNER or ADMIN role required.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri","maxLength":2048},"events":{"type":"array","items":{"type":"string","enum":["verification.created","verification.completed","verification.approved","verification.rejected","verification.manual_review","verification.updated","step.completed","step.failed"]},"minItems":1,"uniqueItems":true},"secret":{"type":"string","minLength":16,"maxLength":256,"description":"Signing secret. Auto-generated if omitted."}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/webhooks/{id}":{"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook endpoint","tags":["webhooks"],"description":"Removes the endpoint. Pending deliveries are not retried after deletion. OWNER or ADMIN role required.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/webhooks/{id}/test":{"post":{"operationId":"testWebhook","summary":"Send a test event to a webhook endpoint","tags":["webhooks"],"description":"Dispatches a synthetic `verification.created` event to the endpoint so you can verify your signature-check code end-to-end. Requires at least one real verification in the org (the test event references its id).","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/workflows":{"get":{"operationId":"listWorkflows","summary":"List workflows","tags":["workflows"],"description":"Returns every workflow defined on the calling organization. Workflows define the verification pipeline (which document checks, which gov-DB lookups, which thresholds).","security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}},"post":{"operationId":"createWorkflow","summary":"Create a workflow","tags":["workflows"],"description":"Defines a new verification pipeline. The `steps` array drives which checks run for each verification using this workflow (document OCR, gov-DB lookups, AML screening, face match, etc.). OWNER or ADMIN role required.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","steps"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":1000},"steps":{"type":"array","items":{"type":"object","required":["type","order"],"properties":{"id":{"type":"string"},"type":{"type":"string","enum":["DOCUMENT_OCR","FACE_MATCH","GOVERNMENT_VERIFY","LIVENESS"]},"order":{"type":"integer","minimum":0},"config":{"type":"object","additionalProperties":true}},"additionalProperties":false},"minItems":1}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/workflows/{id}":{"get":{"operationId":"getWorkflow","summary":"Get a workflow by id","tags":["workflows"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}},"patch":{"operationId":"updateWorkflow","summary":"Update a workflow","tags":["workflows"],"description":"Partially update a workflow. Changes apply to all *future* verifications using this workflow; verifications already in flight keep the workflow snapshot from when they were created. OWNER or ADMIN role required.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":1000},"steps":{"type":"array","items":{"type":"object","required":["type","order"],"properties":{"id":{"type":"string"},"type":{"type":"string","enum":["DOCUMENT_OCR","FACE_MATCH","GOVERNMENT_VERIFY","LIVENESS"]},"order":{"type":"integer","minimum":0},"config":{"type":"object","additionalProperties":true}},"additionalProperties":false},"minItems":1}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}},"delete":{"operationId":"deleteWorkflow","summary":"Soft-delete a workflow","tags":["workflows"],"description":"Marks the workflow inactive. Existing verifications keep working; new verifications cannot reference it. OWNER or ADMIN role required.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/organizations/me":{"get":{"operationId":"getCurrentOrganization","summary":"Get the calling organization","tags":["organizations"],"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}},"patch":{"operationId":"updateOrganization","summary":"Update organization settings","tags":["organizations"],"description":"Updates the organization name, slug, or settings. OWNER or ADMIN role required.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":100},"dataRetentionDays":{"type":"integer","minimum":30,"maximum":3650}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/organizations/me/api-keys":{"get":{"operationId":"listApiKeys","summary":"List API keys","tags":["organizations"],"description":"Returns metadata for every API key in the org (id, name, prefix, last-used). Key hashes are NEVER returned.","security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}},"post":{"operationId":"createApiKey","summary":"Create a new API key","tags":["organizations"],"description":"Provisions a new API key for server-to-server use. The raw key is returned ONCE in this response — store it immediately, it can never be recovered. OWNER or ADMIN role required.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100},"rateLimit":{"type":"integer","minimum":0,"maximum":1000,"default":0}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/organizations/me/api-keys/{id}":{"delete":{"operationId":"revokeApiKey","summary":"Revoke an API key","tags":["organizations"],"description":"Permanently revokes the key. The currently-authenticated key cannot revoke itself. OWNER or ADMIN role required.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"4XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}},"5XX":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/def-0"}}}}}}},"/v1/otp/send":{"post":{"summary":"Send OTP to phone (SMS) or email","tags":["otp"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["verificationId","channel","destination"],"properties":{"verificationId":{"type":"string","format":"uuid"},"channel":{"type":"string","enum":["sms","email"]},"destination":{"type":"string","minLength":1,"maxLength":320}},"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"boolean"},"channel":{"type":"string"},"expiresIn":{"type":"number"}}}}}}}}},"/v1/otp/verify":{"post":{"summary":"Verify a submitted OTP code","tags":["otp"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["verificationId","channel","code"],"properties":{"verificationId":{"type":"string","format":"uuid"},"channel":{"type":"string","enum":["sms","email"]},"code":{"type":"string","minLength":6,"maxLength":6,"pattern":"^[0-9]{6}$"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean"},"attemptsRemaining":{"type":"number"}}}}}}}}},"/v1/aml/screen":{"post":{"summary":"Screen a name against AML watchlists","tags":["aml"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fullName"],"properties":{"fullName":{"type":"string","minLength":2,"maxLength":500},"fuzzyThreshold":{"type":"number","minimum":0,"maximum":1,"default":0.75},"lists":{"type":"array","items":{"type":"string","maxLength":50},"maxItems":20},"country":{"type":"string","minLength":2,"maxLength":2}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/aml/lists":{"get":{"summary":"List available AML watchlists","tags":["aml"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/aml/monitor":{"post":{"summary":"Enable or disable daily AML re-screening for a verification","tags":["aml"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["verificationId","fullName","enabled"],"properties":{"verificationId":{"type":"string","minLength":1,"maxLength":255},"fullName":{"type":"string","minLength":2,"maxLength":500},"enabled":{"type":"boolean"}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/esignature/sign":{"post":{"summary":"Create a signed document package","tags":["esignature"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["verificationId","tier","signerName","documents"],"properties":{"verificationId":{"type":"string","minLength":1,"maxLength":255},"tier":{"type":"string","enum":["name_only","id_and_name","face_id_and_name"]},"signerName":{"type":"string","minLength":2,"maxLength":500},"documents":{"type":"array","items":{"type":"object","required":["title","content"],"properties":{"title":{"type":"string","minLength":1,"maxLength":255},"content":{"type":"string","minLength":1,"maxLength":500000}},"additionalProperties":false},"minItems":1,"maxItems":5},"touchSignature":{"type":"string","maxLength":2000000},"ipAddress":{"type":"string","maxLength":45},"userAgent":{"type":"string","maxLength":1024}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/esignature/{id}":{"get":{"summary":"Get signature details","tags":["esignature"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/esignature/{id}/pdf":{"get":{"summary":"Download the signed PDF certificate","tags":["esignature"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"}}}},"/v1/credit/check":{"post":{"summary":"Run a credit bureau check (Circulo de Credito)","tags":["credit"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fullName"],"properties":{"rfc":{"type":"string","minLength":12,"maxLength":13},"curp":{"type":"string","minLength":18,"maxLength":18},"fullName":{"type":"string","minLength":2,"maxLength":500},"dateOfBirth":{"type":"string","minLength":10,"maxLength":10}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/credit/check/{id}":{"get":{"summary":"Retrieve a credit check result","tags":["credit"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/location/check":{"post":{"summary":"Check IP geolocation, VPN/proxy detection, and geo-restriction","tags":["location"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ipAddress"],"properties":{"ipAddress":{"type":"string","minLength":2,"maxLength":45},"allowedCountries":{"type":"array","items":{"type":"string","minLength":2,"maxLength":2},"maxItems":250},"allowedCities":{"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"maxItems":250},"blockVpn":{"type":"boolean"}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/custom-input/forms":{"post":{"summary":"Create a custom questionnaire form definition","tags":["custom-input"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","fields"],"properties":{"title":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":1000},"fields":{"type":"array","items":{"type":"object","required":["id","type","label","required"],"properties":{"id":{"type":"string","minLength":1,"maxLength":100},"type":{"type":"string","enum":["text","date","checkbox","select"]},"label":{"type":"string","minLength":1,"maxLength":200},"required":{"type":"boolean"},"placeholder":{"type":"string","maxLength":200},"options":{"type":"array","items":{"type":"object","required":["value","label"],"properties":{"value":{"type":"string","maxLength":200},"label":{"type":"string","maxLength":200}},"additionalProperties":false},"maxItems":100},"validation":{"type":"object","properties":{"minLength":{"type":"number","minimum":0},"maxLength":{"type":"number","minimum":1},"pattern":{"type":"string","maxLength":500}},"additionalProperties":false}},"additionalProperties":false},"minItems":1,"maxItems":50}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"formId":{"type":"string"},"title":{"type":"string"},"fieldCount":{"type":"number"},"createdAt":{"type":"string"}}}}}}}}},"/v1/custom-input/submit":{"post":{"summary":"Submit questionnaire form values for a verification step","tags":["custom-input"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["verificationId","formId","values"],"properties":{"verificationId":{"type":"string","minLength":1,"maxLength":255},"formId":{"type":"string","minLength":1,"maxLength":255},"values":{"type":"object","additionalProperties":true}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean"},"submittedAt":{"type":"string"},"errors":{"type":"array","items":{"type":"object","properties":{"fieldId":{"type":"string"},"message":{"type":"string"}}}}}}}}}}}},"/v1/custom-input/{verificationId}":{"get":{"summary":"Retrieve submitted form data for a verification","tags":["custom-input"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"path","name":"verificationId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/openapi.json":{"get":{"summary":"OpenAPI 3.1 specification","tags":["system"],"description":"Returns the full OpenAPI JSON spec generated from registered route schemas. Use this URL as the spec source in Postman, Swagger UI, or code generators.","responses":{"200":{"description":"OpenAPI 3.1 document","content":{"application/json":{"schema":{"type":"object","description":"OpenAPI 3.1 document","additionalProperties":true}}}}}}},"/v1/sdk/session":{"post":{"summary":"Create SDK verification session (public)","tags":["sdk"],"description":"Creates a new verification and returns a short-lived session token. No authentication required.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["clientId","flowId"],"properties":{"clientId":{"type":"string","pattern":"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"},"flowId":{"type":"string","pattern":"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"},"metadata":{"type":"object","additionalProperties":true}},"additionalProperties":false}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/sdk/verifications/{id}":{"get":{"summary":"Get verification status (SDK)","tags":["sdk"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/sdk/verifications/{id}/inputs":{"post":{"summary":"Upload verification documents (SDK)","tags":["sdk"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"202":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/sdk/verifications/{id}/liveness-challenge/start":{"post":{"summary":"Start an active-liveness challenge (issue prompt sequence)","tags":["sdk"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/sdk/verifications/{id}/liveness-challenge/frames":{"post":{"summary":"Upload active-liveness challenge frames","tags":["sdk"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"202":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/sdk/verifications/{id}/events":{"get":{"summary":"Subscribe to verification events (SSE)","tags":["sdk"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/workflows/{id}/theme":{"get":{"summary":"Get workflow theme","tags":["workflows"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}},"put":{"summary":"Create or update workflow theme","tags":["workflows"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"logoUrl":{"type":"string","maxLength":2048},"fontFamily":{"type":"string","maxLength":100},"accentColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"errorColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"warningColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"successColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"backgroundColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"bodyTextColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"titleTextColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"subtitleColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"borderColor":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/audit-logs":{"get":{"summary":"List audit log entries","tags":["organizations"],"parameters":[{"schema":{"type":"string"},"in":"query","name":"resourceType","required":false},{"schema":{"type":"string"},"in":"query","name":"resourceId","required":false},{"schema":{"type":"string"},"in":"query","name":"action","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"limit","required":false},{"schema":{"type":"integer","minimum":0,"default":0},"in":"query","name":"offset","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/admin/queue-stats":{"get":{"summary":"Get worker queue statistics","tags":["organizations"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/documents/{id}/image":{"get":{"summary":"Get document image","tags":["documents"],"parameters":[{"schema":{"type":"string"},"in":"query","name":"key","required":false},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/health/full":{"get":{"summary":"Aggregated health check across all downstream dependencies","tags":["system"],"responses":{"200":{"description":"Default Response"}}}},"/v1/admin/metrics/classifier":{"get":{"summary":"Classifier decision metrics over a rolling window","tags":["organizations"],"parameters":[{"schema":{"type":"string","enum":["24h","7d","30d"]},"in":"query","name":"window","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/admin/metrics/extraction":{"get":{"summary":"Field-extraction decision metrics over a rolling window","tags":["organizations"],"parameters":[{"schema":{"type":"string","enum":["24h","7d","30d"]},"in":"query","name":"window","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/admin/metrics/field-calibration":{"get":{"summary":"Field-extraction calibration: predicted vs empirical RENAPO match rate","tags":["organizations"],"parameters":[{"schema":{"type":"string","enum":["24h","7d","30d"]},"in":"query","name":"window","required":false}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/v1/training/queue":{"get":{"summary":"Training queue overview","tags":["training"],"parameters":[{"schema":{"type":"string"},"in":"query","name":"docType","required":false},{"schema":{"type":"string"},"in":"query","name":"strategy","required":false},{"schema":{"type":"string"},"in":"query","name":"metamapStatus","required":false},{"schema":{"type":"string"},"in":"query","name":"batchId","required":false},{"schema":{"type":"string"},"in":"query","name":"hitRate","required":false},{"schema":{"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false}],"responses":{"200":{"description":"Default Response"}}}},"/v1/training/next":{"get":{"summary":"Next doc to review","tags":["training"],"parameters":[{"schema":{"type":"string"},"in":"query","name":"docType","required":false},{"schema":{"type":"string"},"in":"query","name":"strategy","required":false},{"schema":{"type":"string"},"in":"query","name":"metamapStatus","required":false},{"schema":{"type":"string"},"in":"query","name":"batchId","required":false},{"schema":{"type":"string"},"in":"query","name":"hitRate","required":false},{"schema":{"type":"string"},"in":"query","name":"after","required":false}],"responses":{"200":{"description":"Default Response"}}}},"/v1/training/docs/{docId}":{"get":{"summary":"Get one document with extraction sources","tags":["training"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"docId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/training/media/{mediaId}":{"get":{"summary":"Stream MetaMap media file from disk","tags":["training"],"parameters":[{"schema":{"type":"string"},"in":"query","name":"key","required":false},{"schema":{"type":"string"},"in":"path","name":"mediaId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/training/docs/{docId}/labels":{"post":{"summary":"Save reviewer-confirmed labels","tags":["training"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fields"],"properties":{"documentTypeOverride":{"type":["string","null"]},"fields":{"type":"array","items":{"type":"object","required":["fieldName"],"properties":{"fieldName":{"type":"string"},"confirmedValue":{"type":["string","null"]},"wasMlCorrect":{"type":["boolean","null"]},"wasMetamapCorrect":{"type":["boolean","null"]},"wasFieldPresent":{"type":["boolean","null"]},"notes":{"type":["string","null"]}}}},"flags":{"type":"array","items":{"type":"string"}},"reviewSeconds":{"type":"number"},"reviewNotes":{"type":["string","null"]}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"docId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/training/docs/{docId}/skip":{"post":{"summary":"Skip a doc without labeling","tags":["training"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"docId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/training/batches":{"get":{"summary":"List precompute batches with hit-rate stats","tags":["training"],"responses":{"200":{"description":"Default Response"}}}},"/v1/training/batches/{batchId}":{"get":{"summary":"One batch + its stats","tags":["training"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"batchId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/training/stats":{"get":{"summary":"Reviewer throughput + agreement-rate stats","tags":["training"],"responses":{"200":{"description":"Default Response"}}}},"/v1/dl-groundtruth/upload":{"post":{"responses":{"200":{"description":"Default Response"}}}},"/v1/dl-groundtruth":{"get":{"responses":{"200":{"description":"Default Response"}}}},"/v1/dl-groundtruth/{id}":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}},"patch":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}},"delete":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/dl-groundtruth/{id}/reocr":{"post":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/dl-groundtruth/{id}/image":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/dl-groundtruth/export":{"get":{"responses":{"200":{"description":"Default Response"}}}},"/v1/dl-groundtruth/states":{"get":{"responses":{"200":{"description":"Default Response"}}}},"/v1/selfie-groundtruth/upload":{"post":{"responses":{"200":{"description":"Default Response"}}}},"/v1/selfie-groundtruth":{"get":{"responses":{"200":{"description":"Default Response"}}}},"/v1/selfie-groundtruth/{id}":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}},"patch":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}},"delete":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/selfie-groundtruth/{id}/rematch":{"post":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/selfie-groundtruth/{id}/selfie-image":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/selfie-groundtruth/{id}/document-image":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/selfie-groundtruth/export":{"get":{"responses":{"200":{"description":"Default Response"}}}},"/v1/dl-state-groundtruth":{"get":{"responses":{"200":{"description":"Default Response"}}}},"/v1/dl-state-groundtruth/stats":{"get":{"responses":{"200":{"description":"Default Response"}}}},"/v1/dl-state-groundtruth/{id}":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}},"patch":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/dl-state-groundtruth/{id}/document-image":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/v1/passport-image/{mediaId}":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"mediaId","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/inngest":{"get":{"responses":{"200":{"description":"Default Response"}}},"post":{"responses":{"200":{"description":"Default Response"}}},"put":{"responses":{"200":{"description":"Default Response"}}}}},"servers":[{"url":"https://api.verifymx.xyz","description":"Production"},{"url":"http://localhost:3001","description":"Local development"}],"tags":[{"name":"auth","description":"Authentication and token issuance"},{"name":"verifications","description":"Verification lifecycle"},{"name":"workflows","description":"Workflow configuration"},{"name":"webhooks","description":"Webhook endpoint management"},{"name":"organizations","description":"Organization and API key management"},{"name":"otp","description":"OTP delivery and verification (phone/email)"},{"name":"aml","description":"AML watchlist screening and monitoring"},{"name":"esignature","description":"Electronic signature (3-tier: name / id+name / face+id+name)"},{"name":"credit","description":"Credit bureau checks via Circulo de Credito"},{"name":"location","description":"IP geolocation, VPN/proxy detection, and geo-restriction"},{"name":"custom-input","description":"Configurable questionnaire forms for verification flows"},{"name":"sdk","description":"Public SDK endpoints for end-user identity verification"},{"name":"audit-logs","description":"Read-only audit log of all API and dashboard activity"},{"name":"admin-groundtruth","description":"Internal: labeled groundtruth datasets for OCR training"},{"name":"admin-training","description":"Internal: model training pipeline orchestration"},{"name":"admin-metrics","description":"Internal: precompute batch KPIs and classifier metrics"},{"name":"admin-tools","description":"Internal: synthetic document generation and dev utilities"},{"name":"health","description":"Liveness and readiness probes"}],"externalDocs":{"url":"https://docs.verifymx.xyz","description":"Full reference, integration guides, SDK docs"}}