{"openapi":"3.1.0","info":{"title":"Verfi API","version":"1.0.0","description":"TCPA consent verification API. Capture, claim, and verify proof of consumer consent for lead generation.","contact":{"name":"Verfi","url":"https://verfi.io","email":"support@verfi.io"},"license":{"name":"Proprietary"}},"servers":[{"url":"https://api.verfi.io/tenant/v1","description":"Production"},{"url":"https://api.staging.verfi.io/api/tenant/v1","description":"Staging"}],"security":[{"bearerAuth":[]}],"paths":{"/sessions":{"get":{"operationId":"listSessions","summary":"List claimed sessions","description":"Returns paginated list of sessions claimed by the authenticated tenant.","tags":["Sessions"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"page","in":"query","schema":{"type":"integer","default":1,"minimum":1},"description":"Page number"},{"name":"limit","in":"query","schema":{"type":"integer","default":20,"minimum":1,"maximum":100},"description":"Results per page"},{"name":"status","in":"query","schema":{"type":"string","enum":["claimed","unclaimed","recorded","expired"]},"description":"Filter by session status"}],"responses":{"200":{"description":"Paginated session list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true},"data":{"type":"object","properties":{"sessions":{"type":"array","items":{"$ref":"#/components/schemas/SessionSummary"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/sessions/{verfiID}/claim":{"post":{"operationId":"claimSession","summary":"Claim a session","description":"Claims an unclaimed session for the authenticated tenant. Starts 3-year retention by default. Requires `sessions:claim` scope.","tags":["Sessions"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/verfiID"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"expirationDate":{"type":"string","format":"date-time","description":"Custom expiration date (ISO 8601). Max 5 years from claim date. Defaults to 3 years."}}}}}},"responses":{"200":{"description":"Session claimed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true},"data":{"type":"object","properties":{"verfiID":{"type":"string","example":"VF-a1b2c3d4"},"status":{"type":"string","const":"claimed"},"claimingTenantId":{"type":"string"},"claimedAt":{"type":"string","format":"date-time"},"expirationDate":{"type":"string","format":"date-time"}}},"warnings":{"type":"array","items":{"type":"string"},"description":"Validation warnings (e.g., custom expiration clamped)"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Usage limit exceeded (free tier)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Session already claimed or not in claimable state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/sessions/{verfiID}/unclaim":{"post":{"operationId":"unclaimSession","summary":"Unclaim a session","description":"Releases a claimed session. Retention drops to 30 days. Only the claiming tenant can unclaim. Requires `sessions:unclaim` scope.","tags":["Sessions"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/verfiID"}],"responses":{"200":{"description":"Session unclaimed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true},"data":{"type":"object","properties":{"verfiID":{"type":"string","example":"VF-a1b2c3d4"},"status":{"type":"string","const":"unclaimed"},"claimingTenantId":{"type":"string","const":"unclaimed"},"unclaimedAt":{"type":"string","format":"date-time"},"unclaimedByTenantId":{"type":"string"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Not authorized — session claimed by different tenant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Session not claimed or already unclaimed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/sessions/{verfiID}/expiration":{"put":{"operationId":"updateSessionExpiration","summary":"Update session expiration","description":"Updates the expiration date of a claimed session. Must be 30+ days from now and within 5 years of claim date. Rate limited: 3 updates/month with 24-hour cooldown. Requires `sessions:expiration` scope.","tags":["Sessions"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/verfiID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["expirationDate"],"properties":{"expirationDate":{"type":"string","format":"date-time","description":"New expiration date (ISO 8601). Must be 30+ days from now, max 5 years from claim date."}}}}}},"responses":{"200":{"description":"Expiration updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true},"message":{"type":"string"},"verfiID":{"type":"string"},"expirationDate":{"type":"string","format":"date-time"},"rateLimitInfo":{"type":"object","properties":{"updatesRemaining":{"type":"integer"},"nextUpdateAllowed":{"type":"string","format":"date-time"},"monthlyResetDate":{"type":"string","format":"date-time"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Not authorized — session claimed by different tenant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Session not in claimed state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — cooldown active or monthly limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/sessions/{verfiID}/proof":{"get":{"operationId":"getSessionProof","summary":"Get machine-readable proof","description":"Returns structured consent proof data for a session. All PII is SHA-256 hashed. Designed for AI agents and automated compliance verification. Requires `sessions:proof` scope.","tags":["Sessions"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/verfiID"}],"responses":{"200":{"description":"Proof data","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/ProofData"}}},"example":{"success":true,"data":{"verfiID":"VF-c2f6b89f","status":"unclaimed","consent":{"given":false,"language":null,"tcpa_compliant":false,"one_to_one":false},"session":{"created_at":"2026-03-05T20:41:17.083Z","duration_ms":12450,"page_url":"https://example.com/form","referrer":"https://example.com/"},"interactions":{"total_events":117,"mouse_movements":45,"clicks":8,"scroll_events":12,"form_interactions":6,"keystroke_count":142,"time_to_first_interaction_ms":1200,"time_to_submit_ms":12450},"form_data":{"fields_filled":["email","phone","name"],"checkboxes_checked":1,"consent_checkbox_interacted":true},"device":{"ip_hash":"037312f17ee0395bcdfcd286484b3548...","user_agent":"Mozilla/5.0 ...","screen_resolution":"1920x1080","platform":"MacIntel","timezone":"America/Los_Angeles"},"pii_binding":{"method":"hash","fields_bound":["email","phone"],"hash":"acc09418024e10f0a96012da08e4204b..."},"proof_url":"https://proof.verfi.io/VF-c2f6b89f?token=...","verification":{"integrity":"f9872e02f8eea4b78764c0ac35db9336...","tamper_detected":false}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/sessions/{verfiID}":{"get":{"operationId":"searchSession","summary":"Search for a session","description":"Look up a session by verfiID. Optionally verify email/phone hashes match. Cross-tenant search (any tenant can look up any session). Requires `sessions:search` scope.","tags":["Sessions"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/verfiID"},{"name":"email","in":"query","schema":{"type":"string"},"description":"SHA-256 hash of email to verify against session PII"},{"name":"phone","in":"query","schema":{"type":"string"},"description":"SHA-256 hash of phone to verify against session PII"}],"responses":{"200":{"description":"Search result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true},"data":{"type":"object","properties":{"verfiID":{"type":"string"},"found":{"type":"boolean"},"status":{"type":"string","enum":["recorded","claimed","unclaimed","expired"]},"originatingTenantId":{"type":"string"},"claimingTenantId":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"claimedAt":{"type":"string","format":"date-time","nullable":true},"unclaimedAt":{"type":"string","format":"date-time","nullable":true},"expirationDate":{"type":"string","format":"date-time"},"metadata":{"type":"object","properties":{"userAgent":{"type":"string"},"ipAddress":{"type":"string"}}},"piiDetected":{"type":"boolean"},"verification":{"type":"object","properties":{"consentRules":{"type":"boolean"},"emailMatch":{"type":"boolean","nullable":true},"phoneMatch":{"type":"boolean","nullable":true}}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Secret API key (sk_) passed as Bearer token. Generate keys in the Verfi dashboard under Integration > API Keys."}},"parameters":{"verfiID":{"name":"verfiID","in":"path","required":true,"schema":{"type":"string","pattern":"^VF-[a-fA-F0-9]{8}$","example":"VF-a1b2c3d4"},"description":"Verfi session ID. Also accepts full proof URLs (e.g., https://proof.verfi.io/VF-a1b2c3d4)."}},"schemas":{"SessionSummary":{"type":"object","properties":{"verfiID":{"type":"string","example":"VF-a1b2c3d4"},"status":{"type":"string","enum":["recorded","claimed","unclaimed","expired"]},"createdAt":{"type":"string","format":"date-time"},"claimedAt":{"type":"string","format":"date-time","nullable":true},"expirationDate":{"type":"string","format":"date-time"},"metadata":{"type":"object"},"piiDetected":{"type":"boolean"}}},"Pagination":{"type":"object","properties":{"currentPage":{"type":"integer"},"totalPages":{"type":"integer"},"totalCount":{"type":"integer"},"limit":{"type":"integer"},"hasNextPage":{"type":"boolean"},"hasPrevPage":{"type":"boolean"}}},"ProofData":{"type":"object","description":"Machine-readable consent proof. All PII fields are SHA-256 hashed.","properties":{"verfiID":{"type":"string"},"status":{"type":"string","enum":["recorded","claimed","unclaimed","expired"]},"consent":{"type":"object","properties":{"given":{"type":"boolean","description":"Whether consent was detected"},"language":{"type":"string","nullable":true,"description":"Consent language text if captured"},"tcpa_compliant":{"type":"boolean","description":"Whether consent meets TCPA requirements"},"one_to_one":{"type":"boolean","description":"Whether consent is one-to-one (single company)"}}},"session":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"duration_ms":{"type":"integer"},"page_url":{"type":"string","format":"uri"},"referrer":{"type":"string","nullable":true}}},"interactions":{"type":"object","properties":{"total_events":{"type":"integer"},"mouse_movements":{"type":"integer"},"clicks":{"type":"integer"},"scroll_events":{"type":"integer"},"form_interactions":{"type":"integer"},"keystroke_count":{"type":"integer"},"time_to_first_interaction_ms":{"type":"integer"},"time_to_submit_ms":{"type":"integer"}}},"form_data":{"type":"object","properties":{"fields_filled":{"type":"array","items":{"type":"string"}},"checkboxes_checked":{"type":"integer"},"consent_checkbox_interacted":{"type":"boolean"}}},"device":{"type":"object","properties":{"ip_hash":{"type":"string","description":"SHA-256 hash of IP address"},"user_agent":{"type":"string"},"screen_resolution":{"type":"string","nullable":true},"platform":{"type":"string","nullable":true},"timezone":{"type":"string","nullable":true}}},"pii_binding":{"type":"object","properties":{"method":{"type":"string","const":"hash"},"fields_bound":{"type":"array","items":{"type":"string"}},"hash":{"type":"string","description":"Combined PII hash for verification"}}},"proof_url":{"type":"string","format":"uri","description":"Shareable human-readable proof page"},"verification":{"type":"object","properties":{"integrity":{"type":"string","description":"Integrity hash of session data"},"tamper_detected":{"type":"boolean"}}}}},"Error":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"},"message":{"type":"string"}},"required":["error"]}},"responses":{"BadRequest":{"description":"Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"Missing authorization header","details":"Authorization header with Bearer token is required"}}}},"NotFound":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}