{"openapi":"3.1.0","info":{"title":"AgentFax API","version":"0.1.0","summary":"Agent-friendly fax sending API.","description":"AgentFax exposes fax sending as a stable HTTPS primitive with API keys, idempotency, dry runs, delivery status, webhooks, and a provider abstraction."},"servers":[{"url":"https://agentfax-api.scshome.workers.dev","description":"Production"}],"security":[{"bearerAuth":[]}],"paths":{"/health":{"get":{"operationId":"health","security":[],"responses":{"200":{"description":"Health response"}}}},"/system/readiness":{"get":{"operationId":"readiness","security":[],"summary":"Inspect non-secret production readiness checks","responses":{"200":{"description":"Readiness state"}}}},"/v1/faxes":{"post":{"operationId":"sendFax","summary":"Send or dry-run a fax","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":8,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendFaxRequest"}}}},"responses":{"200":{"description":"Existing idempotent fax response"},"202":{"description":"Fax accepted"},"409":{"description":"Idempotency key was reused with a different request"},"422":{"description":"Validation error"},"429":{"description":"Fax send rate limit exceeded"}}},"get":{"operationId":"listFaxes","summary":"List recent faxes","responses":{"200":{"description":"Fax list"}}}},"/v1/faxes/{fax_id}":{"get":{"operationId":"getFax","summary":"Retrieve a fax","parameters":[{"name":"fax_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Fax detail"},"404":{"description":"Not found"}}}},"/v1/faxes/{fax_id}/cancel":{"post":{"operationId":"cancelFax","summary":"Cancel a queued fax when the upstream provider still permits it","parameters":[{"name":"fax_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Cancel result"},"409":{"description":"Fax cannot be canceled"}}}},"/v1/faxes/{fax_id}/events":{"get":{"operationId":"streamFaxEvents","summary":"Open a WebSocket that replays and streams normalized fax events","description":"Connect with a WebSocket upgrade request and the same bearer API key used for REST calls. The stream emits connected, heartbeat, pong, and fax.event messages.","parameters":[{"name":"fax_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"101":{"description":"WebSocket stream accepted"},"426":{"description":"WebSocket upgrade required"}}}},"/admin/mock/telnyx/faxes/{fax_id}/events":{"post":{"operationId":"adminCreateMockTelnyxEvent","security":[{"bearerAuth":[]}],"summary":"Admin-only helper to inject a mock Telnyx fax status event","parameters":[{"name":"fax_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"account_id":{"type":"string"},"status":{"type":"string","enum":["queued","media.processed","sending","delivered","failed","canceled"]},"event_type":{"type":"string"},"provider_fax_id":{"type":"string"},"payload":{"type":"object"}}}}}},"responses":{"200":{"description":"Mock event accepted and normalized"}}}},"/billing/checkout":{"post":{"operationId":"createCheckout","summary":"Create a Stripe Checkout session for the authenticated account","responses":{"200":{"description":"Checkout URL"}}}},"/billing/onboarding":{"post":{"operationId":"createOnboardingCheckout","security":[],"summary":"Create a pending account and Stripe Checkout session","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","email"],"properties":{"name":{"type":"string","maxLength":120},"email":{"type":"string","format":"email"},"success_url":{"type":"string","format":"uri"},"cancel_url":{"type":"string","format":"uri"}}}}}},"responses":{"201":{"description":"Pending account id, one-time claim token, and Checkout URL"},"429":{"description":"Onboarding rate limit exceeded"}}}},"/billing/claim":{"post":{"operationId":"claimCheckoutSession","security":[],"summary":"Verify a completed Checkout session and return the first API key once","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["checkout_session_id","claim_token"],"properties":{"checkout_session_id":{"type":"string"},"claim_token":{"type":"string","description":"One-time token returned by /billing/onboarding.","writeOnly":true}}}}}},"responses":{"200":{"description":"Activated account and first API key"},"409":{"description":"Checkout is incomplete, unpaid, or already claimed"}}}},"/billing/portal":{"post":{"operationId":"createBillingPortal","summary":"Create a Stripe Billing Portal session for the authenticated account","responses":{"200":{"description":"Portal URL"},"409":{"description":"Account is not linked to a Stripe customer yet"}}}},"/v1/webhooks":{"get":{"operationId":"listWebhookEndpoints","summary":"List customer webhook endpoints","responses":{"200":{"description":"Webhook endpoint list"}}},"post":{"operationId":"createWebhookEndpoint","summary":"Create a customer webhook endpoint and return its signing secret once","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","pattern":"^https://"},"enabled":{"type":"boolean","default":true}}}}}},"responses":{"201":{"description":"Webhook endpoint plus signing secret"}}}},"/v1/webhooks/{webhook_id}":{"patch":{"operationId":"updateWebhookEndpoint","summary":"Update a customer webhook endpoint","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Updated webhook endpoint"}}},"delete":{"operationId":"disableWebhookEndpoint","summary":"Disable a customer webhook endpoint","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Disabled webhook endpoint"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"SendFaxRequest":{"type":"object","additionalProperties":false,"required":["to"],"properties":{"to":{"type":"string","pattern":"^\\+[1-9]\\d{7,14}$","description":"Recipient fax number in E.164 format."},"from":{"type":"string","pattern":"^\\+[1-9]\\d{7,14}$","description":"Optional sender fax number. It must match the sender assigned to the AgentFax account."},"media_url":{"type":"string","format":"uri","pattern":"^https://","description":"Publicly reachable HTTPS PDF/TIFF/JPEG/PNG/DOC/DOCX/RTF/TXT URL."},"media_name":{"type":"string","description":"Provider-side media name when using uploaded provider media."},"html":{"type":"string","maxLength":100000,"description":"HTML content. Accepted for validation and dry runs; live sending currently requires media_url or media_name."},"text":{"type":"string","maxLength":100000,"description":"Plain-text content. Accepted for validation and dry runs; live sending currently requires media_url or media_name."},"cover_page":{"type":"object","properties":{"to_name":{"type":"string","maxLength":120},"from_name":{"type":"string","maxLength":120},"subject":{"type":"string","maxLength":180},"message":{"type":"string","maxLength":2500}}},"dry_run":{"type":"boolean","default":false,"description":"Validate and record the fax without sending upstream."},"client_reference":{"type":"string","description":"Stable caller reference for reconciliation."},"idempotency_key":{"type":"string","minLength":8,"maxLength":128,"description":"Body alternative to the Idempotency-Key header."},"quality":{"type":"string","enum":["normal","high","very_high","ultra_light","ultra_dark"],"default":"high"},"store_media":{"type":"boolean","default":false},"store_preview":{"type":"boolean","default":false},"webhook_url":{"type":"string","format":"uri","pattern":"^https://","description":"Optional one-off AgentFax delivery webhook URL for this fax. Provider webhooks still route through AgentFax first."},"metadata":{"type":"object","additionalProperties":{"type":"string"}}}}}}}