From 21be508df678f004aaa7f8caf063b6550e0e5e71 Mon Sep 17 00:00:00 2001 From: bensynapse <118375461+bensynapse@users.noreply.github.com> Date: Thu, 12 Nov 2026 08:46:15 +0200 Subject: [PATCH] Version 0.4.0: one fax by card, the spec at API 1.16.1 New resource one_time_sends on both clients, with a wait helper. The order token goes in X-FFA-Order-Token. Client(keyless=True) calls it with no API key. The saved spec follows the public document at API 1.16.1. Models are regenerated from it. FaxJob.one_time is a OneTimeJob model now. The README names price card launch-v2. truthcheck accepts the one-time amounts only with their date. --- CHANGELOG.md | 16 + README.md | 60 +- pyproject.toml | 4 +- scripts/truthcheck.sh | 19 +- spec/openapi.yaml | 12939 ++++++++++++++++------------- src/faxforagents/__init__.py | 4 +- src/faxforagents/_http.py | 23 +- src/faxforagents/async_client.py | 209 +- src/faxforagents/client.py | 241 +- src/faxforagents/errors.py | 9 + src/faxforagents/models.py | 206 +- tests/fixtures/README.md | 8 + tests/fixtures/one-time.json | 625 ++ tests/test_one_time.py | 282 + tests/test_response_shapes.py | 4 +- tests/test_surface.py | 19 +- 16 files changed, 9014 insertions(+), 5654 deletions(-) create mode 100644 tests/fixtures/one-time.json create mode 100644 tests/test_one_time.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 65efcf2..40427f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 0.4.0 (2026-11-12) + +New resource `one_time_sends` on both clients: one fax paid by card, with no wallet. +Its calls are `config`, `create`, `create_from_quote`, `list`, `iter`, `get`, `wait`, `checkout`, `get_by_token`, `confirm`, `resend_confirmation` and `get_receipt_pdf`. +`create` sends a file as multipart or text as JSON. `wait` polls until the order's `next` is `done`, or the step passed as `until`. +`get`, `wait`, `checkout`, `resend_confirmation` and `get_receipt_pdf` take `order_token`, sent as `X-FFA-Order-Token`. The header name is exported as `ORDER_TOKEN_HEADER`. +`Client(keyless=True)` and `AsyncClient(keyless=True)` build a client with no API key. It sends no `Authorization` header and can call `one_time_sends` only. + +The saved spec follows the public document at API 1.16.1, 144 operations. Models are regenerated from it. +New models: `OneTimeOrder`, `OneTimeOrderCreated`, `OneTimeOrderPage`, `OneTimeFromQuote`, `OneTimeCheckout`, `OneTimeConfirm`, `OneTimeConfirmSummary`, `OneTimeConfirmed`, `OneTimeCard`, `OneTimePrice`, `OneTimeJob` and `InsufficientBalance`. +Changed: `FaxJob.one_time` is a `OneTimeJob` model, where 0.3.2 declared a plain dict. Read `fax.one_time.order_id`, not `fax.one_time["order_id"]`. `Receipt` carries `one_time` too. +Other model names, fields and optionality are kept. + +`PaymentRequiredError.one_time_offer` reads `details["one_time_offer"]` when a server sends it, and is `None` otherwise. API 1.16.1 does not send it. +The package metadata links to the pricing page and the dashboard. The README names price card launch-v2, effective 2026-10-31. + ## 0.3.2 (2026-11-10) `identity.create_api_key` takes `connected_app` on both clients. A key made with `connected_app=True` is bound by the auto-send setting like an OAuth connection. diff --git a/README.md b/README.md index 4d01f0b..b27f322 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,7 @@ each call needs. | `ffa.organizations` | `list`, `current`, `update_current`, `request_data_export`, `request_data_deletion`, `download_export_archive` | | `ffa.members` | `list`, `update`, `remove`, `list_invitations`, `invite`, `revoke_invitation` | | `ffa.oauth` | `list_grants`, `iter_grants`, `revoke_grant` | +| `ffa.one_time_sends` | `config`, `create`, `create_from_quote`, `list`, `iter`, `get`, `wait`, `checkout`, `get_by_token`, `confirm`, `resend_confirmation`, `get_receipt_pdf` | `ffa.request("GET", "/v1/anything")` reaches an operation the resources do not cover yet. @@ -173,6 +174,58 @@ key = ffa.identity.create_api_key("gemini", scopes=["fax:send"], connected_app=T key.connected_app # True, also on ffa.identity.list_api_keys() ``` +## One fax by card + +A person with one fax to send needs no wallet and no key. `one_time_sends` makes an order, opens a hosted +card payment and follows the order until the fax ends. The person pays on the hosted page and clicks the +link mailed to them. The client never sees a card. + +```python +from faxforagents import Client + +with Client(keyless=True) as ffa: # no API key: only one_time_sends answers + config = ffa.one_time_sends.config() # open here? the card and the limits + order = ffa.one_time_sends.create( + to="+12025550123", + sender_name="Dana Example", + email="dana@example.com", # the confirmation link and the receipt go here + accept_aup=True, # the policy and terms that config names + file="form.pdf", # or text="...", text_format="markdown" + ) + token = order.status_token # answered once: keep it + page = ffa.one_time_sends.checkout(order.id, order_token=token) + print(page.checkout_url) # the person pays here, then clicks the mailed link + order = ffa.one_time_sends.wait(order.id, order_token=token, timeout=1800) + print(order.status, order.fax["state"] if order.fax else None) + pdf = ffa.one_time_sends.get_receipt_pdf(order.id, order_token=token) +``` + +`order.next` names the step that is waiting: `checkout`, `confirm`, `wait` or `done`. `wait` polls until +`done`, or until the step you pass as `until`. The card is authorized at checkout and charged only when +the carrier confirms delivery. + +The status token goes in the `X-FFA-Order-Token` header. It reads the order, opens its checkout, mails +the confirmation again and downloads the receipt. A client with a live key of the order's organization +needs no token. A test key is refused: an order is always live. + +With a live key, `create_from_quote` turns a quote the wallet does not cover into an order. + +```python +from faxforagents import PaymentRequiredError + +try: + fax = ffa.faxes.create(quote_id=quote.id) +except PaymentRequiredError: + order = ffa.one_time_sends.create_from_quote( + quote_id=quote.id, sender_name="Dana Example", accept_aup=True, approval_id=approval_id + ) +``` + +Paying by card does not replace approval. The send must have an approval or a delegated policy, as a +wallet send does. `confirm(order_id, token=...)` and `get_by_token(token)` serve a page of your own +that receives the mailed link. The amounts are on the [pricing page](https://faxforagents.com/pricing/) +and in `config().routes`, in micro-units. + ## Webhooks ```python @@ -208,9 +261,10 @@ Every command takes `--json` for the raw answer. ## Prices -Price card launch-v1 is effective from 2026-10-28. $0.039 per page sent, $0.019 per page received. +Price card launch-v2 is effective from 2026-10-31. $0.039 per page sent, $0.019 per page received. $2 per number per month, $19 per month for the Agent workspace. Sandbox pages cost 0. -`ffa.platform.capabilities()` returns the card in force. +`ffa.platform.capabilities()` returns the card in force. One fax by card has its own price: +`ffa.one_time_sends.config()` returns it. ## Working on this repository @@ -223,7 +277,7 @@ ruff check src tests && mypy src sh scripts/truthcheck.sh ``` -`spec/openapi.yaml` follows the public document at API version 1.13.2 and keeps the schemas of the earlier live documents. +`spec/openapi.yaml` follows the public document at API version 1.16.1 and keeps the schemas of the earlier live documents. It also retains earlier public model names and route-documented response additions. To refresh it, merge the live schemas while preserving existing declarations, then run `uv run python scripts/gen_models.py`. Keep new response fields optional so older payloads and existing callers remain valid. diff --git a/pyproject.toml b/pyproject.toml index b06f05f..e5aca8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "faxforagents" -version = "0.3.2" +version = "0.4.0" description = "Python client for Fax for Agents: a fax API, a hosted MCP server and SDKs built so AI agents can send and receive faxes." readme = "README.md" requires-python = ">=3.10" @@ -50,6 +50,8 @@ faxforagents = "faxforagents.cli:main" [project.urls] Homepage = "https://faxforagents.com" Documentation = "https://faxforagents.com/docs" +Pricing = "https://faxforagents.com/pricing" +Dashboard = "https://app.faxforagents.com" Repository = "https://github.com/faxforagents/sdk-python" Issues = "https://github.com/faxforagents/sdk-python/issues" Changelog = "https://github.com/faxforagents/sdk-python/blob/main/CHANGELOG.md" diff --git a/scripts/truthcheck.sh b/scripts/truthcheck.sh index 1fa5a6f..ac30225 100644 --- a/scripts/truthcheck.sh +++ b/scripts/truthcheck.sh @@ -3,10 +3,12 @@ # not on the card. Provenance: faxforagents/openapi scripts/truthcheck.sh (2026-10-28). spec/ is the # public OpenAPI document, checked in its own repository, so it is exempt here. # -# The price card (price_version launch-v1, 2026-10-28): $0.039 per page sent, $0.019 per page +# The price card (price_version launch-v2, effective 2026-10-31): $0.039 per page sent, $0.019 per page # received, $2 per number per month, $19 per month workspace. Any other dollar amount in copy is stale # or invented. Claims the product does not make: HIPAA, "the first", "cheapest". Competitors are never # named. CHANGELOG.md is exempt from the price rule (dated entries quote the card as it stood). +# One fax by card (published 2026-11-11): $2.49 for up to 5 pages to a US or Canadian number, cover +# included, $0.49 per extra page. A file that states either amount states that date too (rule 6). set -u cd "$(dirname "$0")/.." || exit 1 @@ -23,10 +25,10 @@ flag() { prices=$(printf '%s\n' "$FILES" | grep -vE '^tests/' | xargs grep -noE '\$[0-9][0-9,]*(\.[0-9]+)?.?' 2>/dev/null \ | grep -vE '\$[0-9]+[")}]$' \ | sed -E 's/([0-9])[^0-9]$/\1/' \ - | grep -vE ':\$(0\.039|0\.019|2|2\.00|19|19\.00)$' || true) + | grep -vE ':\$(0\.039|0\.019|2|2\.00|19|19\.00|2\.49|0\.49)$' || true) if [ -n "$prices" ]; then printf '%s\n' "$prices" >&2 - flag "a dollar amount that is not on the price card (0.039, 0.019, 2, 19)" + flag "a dollar amount that is not on the price card (0.039, 0.019, 2, 19, and 2.49, 0.49 for one fax by card)" fi # 2) Claims the product does not make. @@ -58,11 +60,18 @@ fi # 5) Anchors that have to be present wherever prices are described. if printf '%s\n' "$FILES" | xargs grep -lq '\$0\.039' 2>/dev/null; then - if ! printf '%s\n' "$FILES" | xargs grep -q '2026-10-28' 2>/dev/null; then - flag "a price is stated without its date (2026-10-28)" + if ! printf '%s\n' "$FILES" | xargs grep -q '2026-10-31' 2>/dev/null; then + flag "a price is stated without its date (2026-10-31)" fi fi +# 6) A one-time amount is stated only in a file that also states the day it was published. +for f in $(printf '%s\n' "$FILES" | xargs grep -lE '\$(2\.49|0\.49)' 2>/dev/null); do + if ! grep -q '2026-11-11' "$f"; then + flag "$f states a one-time price without its date (2026-11-11)" + fi +done + if [ "$fail" -eq 0 ]; then echo "truthcheck: OK" fi diff --git a/spec/openapi.yaml b/spec/openapi.yaml index 91e1285..ade55fc 100644 --- a/spec/openapi.yaml +++ b/spec/openapi.yaml @@ -1,4 +1,4 @@ -# Fax for Agents API, refreshed from the public document of https://github.com/faxforagents/openapi at API 1.13.2. +# Fax for Agents API, refreshed from the public document of https://github.com/faxforagents/openapi at API 1.16.1. # All earlier schemas are kept. Existing SDK routes, model names and serializer additions are retained. openapi: 3.1.0 info: @@ -26,7 +26,7 @@ info: x-logo: url: https://faxforagents.com/brand/logo-512.png altText: Fax for Agents - version: 1.13.2 + version: 1.16.1 servers: - url: https://api.faxforagents.com description: Production (live and test keys) @@ -67,6 +67,8 @@ tags: description: Members of the organization, their roles and invitations. - name: oauth description: 'Connected apps: the OAuth grants of the organization and their revocation.' +- name: one-time-sends + description: 'One fax paid by card, with no wallet: the order, its checkout, the mailed confirmation and the receipt.' security: - bearerAuth: [] - apiKeyHeader: [] @@ -1284,14 +1286,29 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - '401': - description: No key, or a key that was revoked or expired (invalid_token). + '402': + description: The wallet does not cover this fax (insufficient_balance). details names available_micro + and needed_micro. Nothing was held. content: application/json: schema: - $ref: '#/components/schemas/ErrorEnvelope' + $ref: '#/components/schemas/InsufficientBalance' '403': - description: The key lacks the scope fax:send (scope_missing). + description: The key lacks the scope fax:send (scope_missing), or the send needs a person's approval (approval_required). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '409': + description: 'The quote expired (quote_expired), already created a fax (quote_consumed), belongs to a + one-time order (quote_pricing_mismatch), or the document changed since the quote (document_hash_mismatch). + Also idempotency_key_conflict: the Idempotency-Key was used with a different body.' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '401': + description: No key, or a key that was revoked or expired (invalid_token). content: application/json: schema: @@ -1641,6 +1658,59 @@ paths: mutating: false idempotency_key: false agent_key_allowed: true + /v1/faxes/{fax_id}/receipt.pdf: + get: + tags: + - faxes + summary: The same receipt as a one-page PDF + operationId: get_fax_receipt_pdf + parameters: + - name: fax_id + in: path + required: true + schema: + type: string + title: Fax Id + responses: + '200': + description: The receipt as a PDF + content: + application/pdf: {} + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + '401': + description: No key, or a key that was revoked or expired (invalid_token). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '403': + description: The key lacks the scope fax:read (scope_missing). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '404': + description: Not an id of this organization, or it was deleted (not_found). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '429': + description: Rate limit reached (rate_limited). Retry after the seconds in Retry-After. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + x-llm: + scope: fax:read + mutating: false + idempotency_key: false + agent_key_allowed: true /v1/faxes/{fax_id}/cancel: post: tags: @@ -4106,6 +4176,58 @@ paths: mutating: true idempotency_key: true agent_key_allowed: false + /v1/organization/export-archives/{token}: + get: + tags: + - organizations + summary: Download an organization archive with a signed link valid for 72 hours + operationId: download_export_archive + parameters: + - name: token + in: path + required: true + schema: + type: string + title: Token + - name: exp + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Exp + - name: sig + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Sig + responses: + '200': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + '404': + description: No such resource, or the signed link expired. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + x-llm: + scope: signed-url + mutating: false + idempotency_key: false + agent_key_allowed: true + security: [] + description: 'No API key: the `exp` and `sig` query values come from the signed URL the API minted. The link + lives 72 hours.' /v1/members: get: tags: @@ -5037,6 +5159,119 @@ paths: mutating: false idempotency_key: false agent_key_allowed: true + /v1/organizations/current/auto-send: + get: + tags: + - policies + - organizations + summary: Read the auto-send setting for connected apps in this environment + operationId: get_auto_send + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AutoSendSetting' + '401': + description: No key, or a key that was revoked or expired (invalid_token). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '403': + description: The key lacks the scope policy:read (scope_missing). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '404': + description: Not an id of this organization, or it was deleted (not_found). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '429': + description: Rate limit reached (rate_limited). Retry after the seconds in Retry-After. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + x-llm: + scope: policy:read + mutating: false + idempotency_key: false + agent_key_allowed: true + put: + tags: + - policies + - organizations + summary: Let connected apps send without asking, inside two limits (owner) + description: The environment comes from X-FFA-Environment. When enabled, every connected app of the organization + (OAuth connections such as ChatGPT or Claude, and API keys flagged connected_app) sends in that environment + with no approval per fax, inside the amount per fax and the faxes per day. A hosted integration the server + marks requires_human_approval still needs a person. The first enable creates a managed policy. Every later + save is a new policy version with its audit row. A browser session needs a sign-in from the last 15 minutes. + operationId: put_auto_send + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AutoSendUpdate' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/AutoSendSetting' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + '401': + description: No key, or a key that was revoked or expired (invalid_token). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '403': + description: The key lacks the scope org:admin (scope_missing). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '404': + description: Not an id of this organization, or it was deleted (not_found). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '429': + description: Rate limit reached (rate_limited). Retry after the seconds in Retry-After. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + parameters: + - name: Idempotency-Key + in: header + required: false + schema: + type: string + maxLength: 255 + description: 'Replay the first successful response for the same request within 24 hours. Reusing the idempotency + header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: + true on replay.' + x-llm: + scope: org:admin + mutating: true + idempotency_key: true + agent_key_allowed: false + hint: Needs org:admin and policy:admin together. Never an agent key. /v1/policies/{policy_id}: get: tags: @@ -7793,44 +8028,66 @@ paths: mutating: false idempotency_key: false agent_key_allowed: true - /v1/faxes/{fax_id}/receipt.pdf: + /v1/one-time-sends/config: get: tags: - - faxes - summary: The same receipt as a one-page PDF - operationId: get_fax_receipt_pdf - parameters: - - name: fax_id - in: path - required: true - schema: - type: string - title: Fax Id + - one-time-sends + summary: Whether one-time sends are open here, and the card and limits an order is priced and checked by + operationId: get_one_time_config responses: '200': - description: The receipt as a PDF - content: - application/pdf: {} - '422': - description: Validation Error + description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/HTTPValidationError' - '401': - description: No key, or a key that was revoked or expired (invalid_token). + $ref: '#/components/schemas/OneTimeConfig' + '429': + description: Rate limit reached (rate_limited). Retry after the seconds in Retry-After. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' - '403': - description: The key lacks the scope fax:read (scope_missing). - content: + x-llm: + scope: none + mutating: false + idempotency_key: false + agent_key_allowed: true + hint: Read enabled first. The routes and their prices are present only while the product is open here. + security: [] + description: No API key is needed. + /v1/one-time-sends: + post: + tags: + - one-time-sends + summary: 'Create a one-time order: one document to one fax number, priced from the card' + description: No credential is needed. The answer carries the price, the page count and the status token. The + next step is the checkout. A live key with fax:send or a signed-in session may create an order for its organization. + Nothing is charged and nothing is sent by this call. + operationId: create_one_time_order + responses: + '201': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/OneTimeOrderCreated' + '403': + description: A key was sent and lacks fax:send (scope_missing), or the destination is blocked (blocked_destination). + content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' - '404': - description: Not an id of this organization, or it was deleted (not_found). + '422': + description: 'The form was refused (invalid_request), the number is not a fax destination (invalid_destination), + the route is not sold one fax at a time (unsupported_destination), or the document is too large (document_too_large). + A test key is refused: an order is live.' + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '503': + description: One-time sends are switched off on this server (one_time_unavailable). Nothing was created + or charged. content: application/json: schema: @@ -7841,77 +8098,251 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' + parameters: + - name: Idempotency-Key + in: header + required: false + schema: + type: string + maxLength: 255 + description: 'Replay the first successful response for the same request within 24 hours. Reusing the idempotency + header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: + true on replay.' + requestBody: + required: true + content: + multipart/form-data: + schema: + additionalProperties: false + description: |- + The JSON form of an order: text only. A file (PDF, PNG, JPEG, TIFF) goes as ``multipart/form-data`` + with the same field names and the part ``file``. + properties: + email: + anyOf: + - maxLength: 254 + type: string + - type: 'null' + description: Where the confirmation link, the receipt and the evidence go. Required without a + credential; a signed-in session uses its own address; a key may leave it out to use the organization's. + title: Email + sender_name: + description: Printed on the cover sheet. 2 to 120 characters. + maxLength: 200 + title: Sender Name + type: string + to: + description: The destination fax number, E.164. + maxLength: 40 + title: To + type: string + accept_aup: + description: 'True: the sender accepts the acceptable use policy and the terms the config route + names.' + title: Accept Aup + type: boolean + to_name: + anyOf: + - maxLength: 120 + type: string + - type: 'null' + title: To Name + subject: + anyOf: + - maxLength: 200 + type: string + - type: 'null' + title: Subject + message: + anyOf: + - maxLength: 2000 + type: string + - type: 'null' + title: Message + text: + description: The document as plain text or Markdown. + title: Text + type: string + text_format: + default: text + enum: + - text + - markdown + title: Text Format + type: string + delete_after_send: + default: false + description: 'Delete the document and the transmitted copy the moment the fax ends in delivered, + partial or failed. The order, the receipt and the carrier evidence stay; they hold no document + content. A fax that ends unknown keeps the document until the carrier reports, or until the + retention period ends. Default: keep the document for the retention period.' + title: Delete After Send + type: boolean + source_ref: + anyOf: + - maxLength: 32 + type: string + - type: 'null' + description: Where the sender came from (a short tag, never a URL). + title: Source Ref + file: + type: string + format: binary + description: 'The document: PDF, PNG, JPEG or TIFF. Send this or text, not both.' + required: + - sender_name + - to + - accept_aup + type: object + application/json: + schema: + additionalProperties: false + description: |- + The JSON form of an order: text only. A file (PDF, PNG, JPEG, TIFF) goes as ``multipart/form-data`` + with the same field names and the part ``file``. + properties: + email: + anyOf: + - maxLength: 254 + type: string + - type: 'null' + description: Where the confirmation link, the receipt and the evidence go. Required without a + credential; a signed-in session uses its own address; a key may leave it out to use the organization's. + title: Email + sender_name: + description: Printed on the cover sheet. 2 to 120 characters. + maxLength: 200 + title: Sender Name + type: string + to: + description: The destination fax number, E.164. + maxLength: 40 + title: To + type: string + accept_aup: + description: 'True: the sender accepts the acceptable use policy and the terms the config route + names.' + title: Accept Aup + type: boolean + to_name: + anyOf: + - maxLength: 120 + type: string + - type: 'null' + title: To Name + subject: + anyOf: + - maxLength: 200 + type: string + - type: 'null' + title: Subject + message: + anyOf: + - maxLength: 2000 + type: string + - type: 'null' + title: Message + text: + description: The document as plain text or Markdown. + title: Text + type: string + text_format: + default: text + enum: + - text + - markdown + title: Text Format + type: string + delete_after_send: + default: false + description: 'Delete the document and the transmitted copy the moment the fax ends in delivered, + partial or failed. The order, the receipt and the carrier evidence stay; they hold no document + content. A fax that ends unknown keeps the document until the carrier reports, or until the + retention period ends. Default: keep the document for the retention period.' + title: Delete After Send + type: boolean + source_ref: + anyOf: + - maxLength: 32 + type: string + - type: 'null' + description: Where the sender came from (a short tag, never a URL). + title: Source Ref + required: + - sender_name + - to + - accept_aup + - text + type: object x-llm: - scope: fax:read - mutating: false - idempotency_key: false + scope: fax:send + mutating: true + idempotency_key: true agent_key_allowed: true - /v1/organization/export-archives/{token}: + hint: 'Charges nothing and sends nothing. Keep status_token: it is answered once. Next is the checkout, + then a person pays on checkout_url and clicks the mailed confirmation link.' + key_optional: true + security: + - {} + - bearerAuth: [] + - apiKeyHeader: [] get: tags: - - organizations - summary: Download an organization archive with a signed link valid for 72 hours - operationId: download_export_archive + - one-time-sends + summary: List the organization's one-time orders, newest first + operationId: list_one_time_orders parameters: - - name: token - in: path - required: true - schema: - type: string - title: Token - - name: exp + - name: cursor in: query required: false schema: anyOf: - type: string - type: 'null' - title: Exp - - name: sig + title: Cursor + - name: limit + in: query + required: false + schema: + type: integer + maximum: 200 + minimum: 1 + default: 50 + title: Limit + - name: status in: query required: false schema: anyOf: - - type: string + - enum: + - draft + - checkout_open + - authorized + - awaiting_confirmation + - released + - capturing + - canceling + - captured + - released_hold + - expired + - refused + - settle_failed + type: string - type: 'null' - title: Sig + title: Status responses: '200': description: Successful Response - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - '404': - description: No such resource, or the signed link expired. content: application/json: schema: - $ref: '#/components/schemas/ErrorEnvelope' - x-llm: - scope: signed-url - mutating: false - idempotency_key: false - agent_key_allowed: true - security: [] - description: 'No API key: the `exp` and `sig` query values come from the signed URL the API minted. The link - lives 72 hours.' - /v1/organizations/current/auto-send: - get: - tags: - - policies - - organizations - summary: Read the auto-send setting for connected apps in this environment - operationId: get_auto_send - responses: - '200': - description: Successful Response + $ref: '#/components/schemas/OneTimeOrderPage' + '422': + description: Validation Error content: application/json: schema: - $ref: '#/components/schemas/AutoSendSetting' + $ref: '#/components/schemas/HTTPValidationError' '401': description: No key, or a key that was revoked or expired (invalid_token). content: @@ -7919,7 +8350,7 @@ paths: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': - description: The key lacks the scope policy:read (scope_missing). + description: The key lacks the scope fax:read (scope_missing). content: application/json: schema: @@ -7937,48 +8368,61 @@ paths: schema: $ref: '#/components/schemas/ErrorEnvelope' x-llm: - scope: policy:read + scope: fax:read mutating: false idempotency_key: false agent_key_allowed: true - put: + /v1/one-time-sends/from-quote: + post: tags: - - policies - - organizations - summary: Let connected apps send without asking, inside two limits (owner) - description: The environment comes from X-FFA-Environment. When enabled, every connected app of the organization - (OAuth connections such as ChatGPT or Claude, and API keys flagged connected_app) sends in that environment - with no approval per fax, inside the amount per fax and the faxes per day. A hosted integration the server - marks requires_human_approval still needs a person. The first enable creates a managed policy. Every later - save is a new policy version with its audit row. A browser session needs a sign-in from the last 15 minutes. - operationId: put_auto_send + - one-time-sends + summary: Create a one-time order from a live quote the wallet does not cover + description: 'The quote supplies the document, the destination and the cover. The price is the card''s one-time + price, not the quote''s. The quote then belongs to the order. Needs a live key with fax:send. A key needs + what a wallet send of the quote needs: approval_id, or a delegated policy that allows the send. Paying by + card replaces neither. Without one the answer is 403 approval_required.' + operationId: create_one_time_order_from_quote requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/AutoSendUpdate' + $ref: '#/components/schemas/OneTimeFromQuote' responses: - '200': + '201': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/AutoSendSetting' + $ref: '#/components/schemas/OneTimeOrderCreated' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - '401': - description: No key, or a key that was revoked or expired (invalid_token). + '403': + description: The key lacks fax:send (scope_missing), or the send has no approval and no policy allows + it (approval_required). content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' - '403': - description: The key lacks the scope org:admin (scope_missing). + '409': + description: The quote expired (quote_expired), or the document changed since the quote (document_hash_mismatch). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '503': + description: One-time sends are switched off on this server (one_time_unavailable). Nothing was created + or charged. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '401': + description: No key, or a key that was revoked or expired (invalid_token). content: application/json: schema: @@ -8006,63 +8450,105 @@ paths: header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: true on replay.' x-llm: - scope: org:admin + scope: fax:send mutating: true idempotency_key: true - agent_key_allowed: false - hint: Needs org:admin and policy:admin together. Never an agent key. - /v1/oauth/grants: + agent_key_allowed: true + hint: For a live quote the wallet does not cover. The price is the one-time price of the card, not the quote's. + A key still needs approval_id or a delegated policy that allows the send. + /v1/one-time-sends/by-token/{token}: get: tags: - - oauth - summary: Your connected apps in this organization - operationId: list_oauth_grants + - one-time-sends + summary: What a confirmation link is for, without using it + description: |- + The confirm page reads this before the click. Nothing is consumed by reading it. + + No API key is needed. + operationId: get_one_time_order_by_token parameters: - - name: cursor - in: query - required: false - schema: - anyOf: - - type: string - maxLength: 64 - - type: 'null' - title: Cursor - - name: limit - in: query - required: false + - name: token + in: path + required: true schema: - type: integer - maximum: 100 - minimum: 1 - default: 100 - title: Limit + type: string + title: Token responses: '200': description: Successful Response content: application/json: schema: - $ref: '#/components/schemas/GrantList' + $ref: '#/components/schemas/OneTimeConfirmSummary' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - '401': - description: No key, or a key that was revoked or expired (invalid_token). + '404': + description: The confirmation link is not valid (link_invalid). content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' - '403': - description: The key lacks the scope key:admin (scope_missing). + '429': + description: Rate limit reached (rate_limited). Retry after the seconds in Retry-After. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' + x-llm: + scope: none + mutating: false + idempotency_key: false + agent_key_allowed: true + security: [] + /v1/one-time-sends/{one_time_order_id}: + get: + tags: + - one-time-sends + summary: 'Read a one-time order: where it stands, what to do next, and the fax with its evidence' + operationId: get_one_time_order + parameters: + - name: one_time_order_id + in: path + required: true + schema: + type: string + title: One Time Order Id + - name: t + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: The status token, when no header carries it. + title: T + description: The status token, when no header carries it. + - name: X-FFA-Order-Token + in: header + required: false + schema: + type: string + description: The status token of the order, for a caller without a key or a session. + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/OneTimeOrder' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' '404': - description: Not an id of this organization, or it was deleted (not_found). + description: No such order. A wrong token, another organization's key and an unknown id all answer this + (not_found). content: application/json: schema: @@ -8074,56 +8560,159 @@ paths: schema: $ref: '#/components/schemas/ErrorEnvelope' x-llm: - scope: key:admin + scope: fax:read mutating: false idempotency_key: false - agent_key_allowed: false - hint: Answers 404 while OAuth connections are not enabled on the server. - /v1/oauth/grants/{grant_id}: - delete: + agent_key_allowed: true + hint: Poll this. next names the step that is waiting, fax carries the job and its evidence once released. + order_token: true + security: + - bearerAuth: [] + - apiKeyHeader: [] + - orderToken: [] + /v1/one-time-sends/{one_time_order_id}/receipt.pdf: + get: tags: - - oauth - summary: Revoke a connected app - operationId: revoke_oauth_grant + - one-time-sends + summary: The receipt of a one-time order's fax as a one-page PDF + description: 'The delivery receipt of the order''s fax: the state, the pages, the document hash, the carrier''s + evidence as it was reported, and what became of the card. It says charged only once the payment provider + confirmed the capture. A key or session of the order''s organization may read it, and so may the status + token, as the header or as ?t=. 409 conflict while the fax has not ended or the order has no fax.' + operationId: get_one_time_receipt_pdf parameters: - - name: grant_id + - name: one_time_order_id in: path required: true schema: type: string - title: Grant Id - - name: Idempotency-Key + title: One Time Order Id + - name: t + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: The status token, when no header carries it. + title: T + description: The status token, when no header carries it. + - name: X-FFA-Order-Token in: header required: false schema: type: string - maxLength: 255 - description: 'Replay the first successful response for the same request within 24 hours. Reusing the idempotency - header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: - true on replay.' + description: The status token of the order, for a caller without a key or a session. responses: - '204': - description: Successful Response + '200': + description: The receipt as a PDF + content: + application/pdf: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - '401': - description: No key, or a key that was revoked or expired (invalid_token). + '409': + description: 'No receipt yet: the order has no fax, or the fax has not ended (conflict).' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' - '403': - description: The key lacks the scope key:admin (scope_missing). + '404': + description: No such order. A wrong token, another organization's key and an unknown id all answer this + (not_found). content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' - '404': - description: Not an id of this organization, or it was deleted (not_found). + '429': + description: Rate limit reached (rate_limited). Retry after the seconds in Retry-After. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + x-llm: + scope: fax:read + mutating: false + idempotency_key: false + agent_key_allowed: true + order_token: true + security: + - bearerAuth: [] + - apiKeyHeader: [] + - orderToken: [] + /v1/one-time-sends/{one_time_order_id}/checkout: + post: + tags: + - one-time-sends + summary: Open the card payment of a one-time order + description: 'Answers the hosted payment page of the order. The card is authorized there, not charged: it + is charged when the carrier confirms delivery. A second call answers the same page while it can still be + paid, and a new one after it expired.' + operationId: create_one_time_checkout + parameters: + - name: one_time_order_id + in: path + required: true + schema: + type: string + title: One Time Order Id + - name: t + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: The status token, when no header carries it. + title: T + description: The status token, when no header carries it. + - name: Idempotency-Key + in: header + required: false + schema: + type: string + maxLength: 255 + description: 'Replay the first successful response for the same request within 24 hours. Reusing the idempotency + header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: + true on replay.' + - name: X-FFA-Order-Token + in: header + required: false + schema: + type: string + description: The status token of the order, for a caller without a key or a session. + responses: + '201': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/OneTimeCheckout' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + '409': + description: The order is already paid (already_paid), or it ended (order_closed). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '503': + description: One-time sends are switched off on this server (one_time_unavailable). Nothing was created + or charged. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '404': + description: No such order. A wrong token, another organization's key and an unknown id all answer this + (not_found). content: application/json: schema: @@ -8135,153 +8724,394 @@ paths: schema: $ref: '#/components/schemas/ErrorEnvelope' x-llm: - scope: key:admin + scope: fax:send mutating: true idempotency_key: true - agent_key_allowed: false -components: - securitySchemes: - bearerAuth: - type: http - scheme: bearer - description: '`Authorization: Bearer ffa_live_...` or `ffa_test_...`. The preferred form.' - apiKeyHeader: - type: apiKey - in: header - name: X-API-Key - description: The same key in `X-API-Key`, for clients that cannot set `Authorization`. - schemas: - Agent: - properties: - id: - type: string - title: Id - name: + agent_key_allowed: true + hint: The card is authorized on the hosted page and charged only when the carrier confirms delivery. A person + pays, never the agent. + order_token: true + security: + - bearerAuth: [] + - apiKeyHeader: [] + - orderToken: [] + /v1/one-time-sends/{one_time_order_id}/confirm: + post: + tags: + - one-time-sends + summary: 'Confirm a one-time order with the token of the mailed link: the fax is released' + description: |- + Proves the sender's address and releases the fax. In a browser without a session it also signs the payer in and sets the session cookies. Idempotency-Key is accepted but ignored: the link works once. A repeat by the same signed-in person answers the confirmed order again. Any other repeat answers 409 link_used and sends nothing. + + No API key is needed. + operationId: confirm_one_time_order + parameters: + - name: one_time_order_id + in: path + required: true + schema: type: string - title: Name - purpose: - anyOf: - - type: string - - type: 'null' - title: Purpose - enabled: - type: boolean - title: Enabled - status: + title: One Time Order Id + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/OneTimeConfirm' + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/OneTimeConfirmed' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + '404': + description: The confirmation link is not valid for this order (link_invalid). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '409': + description: The link was already used (link_used), expired (link_expired), or the browser is signed in + as someone else (link_other_session). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '503': + description: One-time sends are switched off on this server (one_time_unavailable). Nothing was created + or charged. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '429': + description: Rate limit reached (rate_limited). Retry after the seconds in Retry-After. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + x-llm: + scope: none + mutating: true + idempotency_key: false + agent_key_allowed: true + hint: The token comes from the mailed link and works once. This releases the fax. + security: [] + /v1/one-time-sends/{one_time_order_id}/resend-confirmation: + post: + tags: + - one-time-sends + summary: Mail the confirmation link of a one-time order again + description: At most three times per order and once in ten minutes. The earlier links keep working. + operationId: resend_one_time_confirmation + parameters: + - name: one_time_order_id + in: path + required: true + schema: type: string - title: Status - project_id: - anyOf: - - type: string - - type: 'null' - title: Project Id - policy_ids: - items: - type: string - type: array - title: Policy Ids - contact_email: - anyOf: - - type: string - - type: 'null' - title: Contact Email - budget_id: + title: One Time Order Id + - name: t + in: query + required: false + schema: anyOf: - type: string - type: 'null' - title: Budget Id - budgets: - items: - additionalProperties: true - type: object - type: array - title: Budgets - keys: - items: - $ref: '#/components/schemas/ApiKey' - type: array - title: Keys - created_by: - type: string - title: Created By - created_at: + description: The status token, when no header carries it. + title: T + description: The status token, when no header carries it. + - name: Idempotency-Key + in: header + required: false + schema: type: string - title: Created At - updated_at: + maxLength: 255 + description: 'Replay the first successful response for the same request within 24 hours. Reusing the idempotency + header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: + true on replay.' + - name: X-FFA-Order-Token + in: header + required: false + schema: type: string - title: Updated At - disabled_at: + description: The status token of the order, for a caller without a key or a session. + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/OneTimeOrder' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + '409': + description: The order is not waiting for its confirmation (order_closed). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '503': + description: One-time sends are switched off on this server (one_time_unavailable). Nothing was created + or charged. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '404': + description: No such order. A wrong token, another organization's key and an unknown id all answer this + (not_found). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '429': + description: Rate limit reached (rate_limited). Retry after the seconds in Retry-After. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + x-llm: + scope: fax:send + mutating: true + idempotency_key: true + agent_key_allowed: true + hint: At most three times per order and once in ten minutes. + order_token: true + security: + - bearerAuth: [] + - apiKeyHeader: [] + - orderToken: [] + /v1/oauth/grants: + get: + tags: + - oauth + summary: Your connected apps in this organization + operationId: list_oauth_grants + parameters: + - name: cursor + in: query + required: false + schema: anyOf: - type: string + maxLength: 64 - type: 'null' - title: Disabled At - additionalProperties: true - type: object - required: - - id - - name - - purpose - - enabled - - status - - project_id - - policy_ids - - contact_email - - budget_id - - budgets - - keys - - created_by - - created_at - - updated_at - - disabled_at - title: Agent - AgentActivity: + title: Cursor + - name: limit + in: query + required: false + schema: + type: integer + maximum: 100 + minimum: 1 + default: 100 + title: Limit + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/GrantList' + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + '401': + description: No key, or a key that was revoked or expired (invalid_token). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '403': + description: The key lacks the scope key:admin (scope_missing). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '404': + description: Not an id of this organization, or it was deleted (not_found). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '429': + description: Rate limit reached (rate_limited). Retry after the seconds in Retry-After. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + x-llm: + scope: key:admin + mutating: false + idempotency_key: false + agent_key_allowed: false + hint: Answers 404 while OAuth connections are not enabled on the server. + /v1/oauth/grants/{grant_id}: + delete: + tags: + - oauth + summary: Revoke a connected app + operationId: revoke_oauth_grant + parameters: + - name: grant_id + in: path + required: true + schema: + type: string + title: Grant Id + - name: Idempotency-Key + in: header + required: false + schema: + type: string + maxLength: 255 + description: 'Replay the first successful response for the same request within 24 hours. Reusing the idempotency + header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: + true on replay.' + responses: + '204': + description: Successful Response + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + '401': + description: No key, or a key that was revoked or expired (invalid_token). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '403': + description: The key lacks the scope key:admin (scope_missing). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '404': + description: Not an id of this organization, or it was deleted (not_found). + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + '429': + description: Rate limit reached (rate_limited). Retry after the seconds in Retry-After. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + x-llm: + scope: key:admin + mutating: true + idempotency_key: true + agent_key_allowed: false +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + description: '`Authorization: Bearer ffa_live_...` or `ffa_test_...`. The preferred form.' + apiKeyHeader: + type: apiKey + in: header + name: X-API-Key + description: The same key in `X-API-Key`, for clients that cannot set `Authorization`. + orderToken: + type: apiKey + in: header + name: X-FFA-Order-Token + description: The status token of one one-time order, answered once by the create. It reads that order, opens + its checkout and mails its confirmation link again, for a caller with no key. The same value is accepted + as the query parameter `t`. + schemas: + AbuseReview: properties: id: type: string title: Id - actor: + org_id: type: string - title: Actor + title: Org Id + key_id: + anyOf: + - type: string + - type: 'null' + title: Key Id + status: + type: string + enum: + - open + - resolved + title: Status action: type: string + enum: + - none + - suspended title: Action - resource_type: + opened_by: type: string - title: Resource Type - resource_id: - anyOf: - - type: string - - type: 'null' - title: Resource Id - data: - additionalProperties: true - type: object - title: Data - correlation_id: + title: Opened By + additionalProperties: true + type: object + required: + - id + - org_id + - status + - action + - opened_by + title: AbuseReview + AbuseReviewCreate: + properties: + org_id: + type: string + maxLength: 64 + minLength: 3 + title: Org Id + key_id: anyOf: - type: string + maxLength: 64 - type: 'null' - title: Correlation Id - created_at: + title: Key Id + reason: type: string - title: Created At - additionalProperties: true + maxLength: 500 + minLength: 3 + title: Reason + description: 'Why: recorded on the audit row' + suspend: + type: boolean + title: Suspend + description: Suspend sends for the organization or key at once + default: false + additionalProperties: false type: object required: - - id - - actor - - action - - resource_type - - resource_id - - data - - correlation_id - - created_at - title: AgentActivity - AgentActivityPage: + - org_id + - reason + title: AbuseReviewCreate + AbuseReviewPage: properties: items: items: - $ref: '#/components/schemas/AgentActivity' + $ref: '#/components/schemas/AbuseReview' type: array title: Items next_cursor: @@ -8293,473 +9123,418 @@ components: type: object required: - items - title: AgentActivityPage - AgentCreate: + title: AbuseReviewPage + AbuseReviewPatch: properties: - name: + action: type: string - maxLength: 120 - minLength: 1 - title: Name - purpose: - anyOf: - - type: string - maxLength: 500 - - type: 'null' - title: Purpose - description: Shown to humans, never evaluated. - policy_ids: - items: - type: string - type: array - title: Policy Ids - contact_email: - anyOf: - - type: string - maxLength: 254 - - type: 'null' - title: Contact Email - description: Where decided approvals are mailed. - additionalProperties: false - type: object - required: - - name - title: AgentCreate - AgentKeyCreate: + enum: + - suspend + - unsuspend + - resolve + title: Action + reason: + type: string + maxLength: 500 + minLength: 3 + title: Reason + description: 'Why: recorded on the audit row' + additionalProperties: false + type: object + required: + - action + - reason + title: AbuseReviewPatch + Accepted: properties: - label: + organization: + $ref: '#/components/schemas/OrganizationSummary' + additionalProperties: true + type: object + required: + - organization + title: Accepted + Adjustment: + properties: + id: type: string - maxLength: 120 - minLength: 1 - title: Label - default: agent key + title: Id + org_id: + type: string + title: Org Id environment: type: string - enum: - - live - - test title: Environment - default: test - scopes: + direction: + type: string + title: Direction + amount_micro: + type: integer + title: Amount Micro + status: + type: string + title: Status + proposed_by: + type: string + title: Proposed By + applied_by: anyOf: - - items: - type: string - type: array + - type: string - type: 'null' - title: Scopes - description: Omit for the default agent set. Never approval:decide, key:admin, policy:admin, agent:admin. - expires_in_days: + title: Applied By + txn_id: anyOf: - - type: integer - maximum: 3650.0 - minimum: 1.0 + - type: string - type: 'null' - title: Expires In Days + title: Txn Id + additionalProperties: true + type: object + required: + - id + - org_id + - environment + - direction + - amount_micro + - status + - proposed_by + title: Adjustment + AdjustmentApply: + properties: + reason: + type: string + maxLength: 500 + minLength: 3 + title: Reason + description: 'Why: recorded on the audit row' + confirm: + type: boolean + title: Confirm + description: Required when the proposer applies their own adjustment + default: false additionalProperties: false type: object - title: AgentKeyCreate - required: [] - AgentList: + required: + - reason + title: AdjustmentApply + AdjustmentCreate: + properties: + org_id: + type: string + maxLength: 64 + minLength: 3 + title: Org Id + environment: + type: string + enum: + - live + - test + title: Environment + default: live + direction: + type: string + enum: + - credit + - debit + title: Direction + amount_micro: + type: integer + maximum: 10000000000.0 + exclusiveMinimum: 0.0 + title: Amount Micro + description: Integer micro-USD; at most 10,000.00 + reason: + type: string + maxLength: 500 + minLength: 3 + title: Reason + description: 'Why: recorded on the audit row' + additionalProperties: false + type: object + required: + - org_id + - direction + - amount_micro + - reason + title: AdjustmentCreate + AdjustmentPage: properties: items: items: - $ref: '#/components/schemas/Agent' + $ref: '#/components/schemas/Adjustment' type: array title: Items + next_cursor: + anyOf: + - type: string + - type: 'null' + title: Next Cursor additionalProperties: true type: object required: - items - title: AgentList - AgentUpdate: + title: AdjustmentPage + AdminAction: properties: - name: + reason: + type: string + maxLength: 500 + minLength: 3 + title: Reason + description: 'Why: recorded on the audit row' + additionalProperties: false + type: object + required: + - reason + title: AdminAction + AdminBrief: + properties: + utc_day: anyOf: - type: string - maxLength: 120 - minLength: 1 - type: 'null' - title: Name - purpose: + title: Utc Day + text: anyOf: - type: string - maxLength: 500 - - type: 'null' - title: Purpose - enabled: - anyOf: - - type: boolean - - type: 'null' - title: Enabled - description: false revokes every key of the agent. - policy_ids: - anyOf: - - items: - type: string - type: array - type: 'null' - title: Policy Ids - contact_email: + title: Text + posted_at: anyOf: - type: string - maxLength: 254 - type: 'null' - title: Contact Email - additionalProperties: false - type: object - title: AgentUpdate - required: [] - ApiKey: - properties: - id: - type: string - title: Id - label: - type: string - title: Label - prefix: - type: string - title: Prefix - environment: - type: string - enum: - - live - - test - title: Environment - project_id: + title: Posted At + channel: anyOf: - type: string - type: 'null' - title: Project Id - agent_id: + title: Channel + status: anyOf: - type: string + enum: + - posted + - failed + - gave_up - type: 'null' - title: Agent Id - scopes: - items: - type: string - type: array - title: Scopes - created_at: - type: string - title: Created At - last_used_at: + title: Status + attempts: + type: integer + title: Attempts + default: 0 + local_day: anyOf: - type: string - type: 'null' - title: Last Used At - expires_at: + title: Local Day + attempted_at: anyOf: - type: string - type: 'null' - title: Expires At - revoked_at: + title: Attempted At + attempt_utc_day: anyOf: - type: string - type: 'null' - title: Revoked At - rotated_from_id: + title: Attempt Utc Day + retry_after_s: + type: integer + title: Retry After S + default: 600 + waiting_day: anyOf: - type: string - type: 'null' - title: Rotated From Id - connected_app: - type: boolean - title: Connected App - description: The key belongs to a connected app used over stdio, such as the Gemini CLI extension. The - organization's auto-send setting (Settings, Connected apps) binds it like an OAuth connection. - default: false + title: Waiting Day + additionalProperties: true + type: object + title: AdminBrief + AdminDaily: + properties: + metric: + type: string + title: Metric + description: + type: string + title: Description + days: + type: integer + title: Days + items: + items: + $ref: '#/components/schemas/AdminDailyPoint' + type: array + title: Items additionalProperties: true type: object required: - - id - - label - - prefix - - environment - - project_id - - agent_id - - scopes - - created_at - - last_used_at - - expires_at - - revoked_at - - rotated_from_id - title: ApiKey - ApiKeyCreate: + - metric + - description + - days + - items + title: AdminDaily + AdminDailyPoint: properties: - label: + day: type: string - maxLength: 120 - minLength: 1 - title: Label + title: Day environment: type: string - enum: - - live - - test title: Environment - default: test - scopes: - anyOf: - - items: - type: string - type: array - - type: 'null' - title: Scopes - description: Omit for the default agent set; '*' for all. - expires_in_days: + dims: + additionalProperties: true + type: object + title: Dims + value_num: anyOf: - type: integer - maximum: 3650.0 - minimum: 1.0 - type: 'null' - title: Expires In Days - connected_app: - type: boolean - title: Connected App - description: 'This key is for a connected app such as the Gemini CLI extension: the auto-send setting - binds it. Not available on an agent''s key.' - default: false - additionalProperties: false - type: object - required: - - label - title: ApiKeyCreate - ApiKeyList: - properties: - items: - items: - $ref: '#/components/schemas/ApiKey' - type: array - title: Items + title: Value Num + value_json: + title: Value Json + computed_at: + type: string + title: Computed At additionalProperties: true type: object required: - - items - title: ApiKeyList - ApiKeyWithSecret: + - day + - environment + - dims + - computed_at + title: AdminDailyPoint + AdminJob: properties: id: type: string title: Id - label: + org_id: type: string - title: Label - prefix: + title: Org Id + state: type: string - title: Prefix + title: State + route: + type: string + title: Route environment: type: string enum: - live - test title: Environment - project_id: - anyOf: - - type: string - - type: 'null' - title: Project Id - agent_id: - anyOf: - - type: string - - type: 'null' - title: Agent Id - scopes: - items: - type: string - type: array - title: Scopes - created_at: - type: string - title: Created At - last_used_at: - anyOf: - - type: string - - type: 'null' - title: Last Used At - expires_at: - anyOf: - - type: string - - type: 'null' - title: Expires At - revoked_at: - anyOf: - - type: string - - type: 'null' - title: Revoked At - rotated_from_id: - anyOf: - - type: string - - type: 'null' - title: Rotated From Id - secret: - type: string - title: Secret - description: Shown once. Store it now; it cannot be read back. - connected_app: - type: boolean - title: Connected App - description: The key belongs to a connected app used over stdio, such as the Gemini CLI extension. The - organization's auto-send setting (Settings, Connected apps) binds it like an OAuth connection. - default: false + age_s: + type: integer + title: Age S additionalProperties: true type: object required: - id - - label - - prefix + - org_id + - state + - route - environment - - project_id - - agent_id - - scopes - - created_at - - last_used_at - - expires_at - - revoked_at - - rotated_from_id - - secret - title: ApiKeyWithSecret - ApprovalDecision: + - age_s + title: AdminJob + AdminJobPage: properties: - reason: + items: + items: + $ref: '#/components/schemas/AdminJob' + type: array + title: Items + next_cursor: anyOf: - type: string - maxLength: 500 - type: 'null' - title: Reason - additionalProperties: false + title: Next Cursor + additionalProperties: true type: object - title: ApprovalDecision - required: [] - ApprovalRequest: + required: + - items + title: AdminJobPage + AdminOrganization: properties: id: type: string title: Id - kind: - type: string - enum: - - send - - help - title: Kind - status: + name: type: string - enum: - - pending - - approved - - rejected - - revoked - - expired - title: Status - quote_id: + title: Name + suspended_at: anyOf: - type: string - type: 'null' - title: Quote Id - bound: + title: Suspended At + internal: + type: boolean + title: Internal + default: false + balances_micro: additionalProperties: true type: object - title: Bound - message: - anyOf: - - type: string - - type: 'null' - title: Message - requested_by: - type: string - title: Requested By - decided_by: - anyOf: - - type: string - - type: 'null' - title: Decided By - reason: - anyOf: - - type: string - - type: 'null' - title: Reason - consumed_by_job_id: - anyOf: - - type: string - - type: 'null' - title: Consumed By Job Id - created_at: - type: string - title: Created At - expires_at: - anyOf: - - type: string - - type: 'null' - title: Expires At - decided_at: - anyOf: - - type: string - - type: 'null' - title: Decided At - notified: - anyOf: - - type: boolean - - type: 'null' - title: Notified - description: 'On creation: whether the organization was mailed. false when the request repeats a pending - one or the organization''s daily cap of request mails is reached; the request is still shown in the - dashboard.' + title: Balances Micro + counts: + additionalProperties: + type: integer + type: object + title: Counts additionalProperties: true type: object required: - id - - kind - - status - - quote_id - - bound - - message - - requested_by - - decided_by - - reason - - consumed_by_job_id - - created_at - - expires_at - - decided_at - title: ApprovalRequest - ApprovalRequestCreate: + - name + - balances_micro + - counts + title: AdminOrganization + AdminOrganizationDetail: properties: - kind: - type: string - enum: - - send - - help - title: Kind - default: send - quote_id: - anyOf: - - type: string - pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - - type: 'null' - title: Quote Id - description: The quote to bind (kind send). - message: - anyOf: - - type: string - maxLength: 2000 - - type: 'null' - title: Message - description: What the person should know (kind help). Shown as data. - fax_job_id: - anyOf: - - type: string - pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - - type: 'null' - title: Fax Job Id - additionalProperties: false + organization: + additionalProperties: true + type: object + title: Organization + members: + items: + additionalProperties: true + type: object + type: array + title: Members + keys: + items: + additionalProperties: true + type: object + type: array + title: Keys + recent_jobs: + items: + additionalProperties: true + type: object + type: array + title: Recent Jobs + ledger: + additionalProperties: true + type: object + title: Ledger + open_reviews: + items: + additionalProperties: true + type: object + type: array + title: Open Reviews + additionalProperties: true type: object - title: ApprovalRequestCreate - required: [] - ApprovalRequestPage: + required: + - organization + - members + - keys + - recent_jobs + - ledger + - open_reviews + title: AdminOrganizationDetail + AdminOrganizationPage: properties: items: items: - $ref: '#/components/schemas/ApprovalRequest' + $ref: '#/components/schemas/AdminOrganization' type: array title: Items next_cursor: @@ -8771,136 +9546,200 @@ components: type: object required: - items - title: ApprovalRequestPage - Attempt: + title: AdminOrganizationPage + AdminRevenue: properties: - id: - type: string - title: Id - fax_job_id: - type: string - title: Fax Job Id - attempt_number: - type: integer - title: Attempt Number - provider: - type: string - title: Provider - provider_ref: - anyOf: - - type: string - - type: 'null' - title: Provider Ref - status: + computed_at: type: string - title: Status - pages_sent: + title: Computed At + days: type: integer - title: Pages Sent - failure_code: - anyOf: - - type: string - - type: 'null' - title: Failure Code - evidence: - anyOf: - - additionalProperties: true + title: Days + captured: + additionalProperties: true + type: object + title: Captured + funded: + additionalProperties: true + type: object + title: Funded + refunded: + additionalProperties: true + type: object + title: Refunded + recurring: + additionalProperties: true + type: object + title: Recurring + balances_micro: + additionalProperties: + additionalProperties: + type: integer type: object - - type: 'null' - title: Evidence - error: - anyOf: - - type: string - - type: 'null' - title: Error - started_at: - type: string - title: Started At - finished_at: - anyOf: - - type: string - - type: 'null' - title: Finished At - additionalProperties: true - type: object - required: - - id - - fax_job_id - - attempt_number - - provider - - provider_ref - - status - - pages_sent - - failure_code - - evidence - - error - - started_at - - finished_at - title: Attempt - AttemptPage: - properties: - items: + type: object + title: Balances Micro + by_day: items: - $ref: '#/components/schemas/Attempt' + additionalProperties: true + type: object type: array - title: Items + title: By Day + data_age: + additionalProperties: true + type: object + title: Data Age + sources: + items: + additionalProperties: true + type: object + type: array + title: Sources additionalProperties: true type: object required: - - items - title: AttemptPage - AutoTopup: + - computed_at + - days + - captured + - funded + - refunded + - recurring + - balances_micro + - by_day + - data_age + - sources + title: AdminRevenue + AdminStats: properties: - id: - type: string - title: Id - environment: - type: string - enum: - - live - - test - title: Environment - status: + computed_at: type: string - enum: - - pending_setup - - active - - disabled - title: Status - threshold_micro: - type: integer - title: Threshold Micro - amount_micro: - type: integer - title: Amount Micro - daily_cap_micro: - type: integer - title: Daily Cap Micro - payment_method_saved: + title: Computed At + jobs_by_state: + additionalProperties: + type: integer + type: object + title: Jobs By State + by_environment: + additionalProperties: true + type: object + title: By Environment + live_sends: + additionalProperties: true + type: object + title: Live Sends + needs_you: + additionalProperties: true + type: object + title: Needs You + window: + additionalProperties: true + type: object + title: Window + totals: + additionalProperties: true + type: object + title: Totals + money_live_micro: + additionalProperties: + type: integer + type: object + title: Money Live Micro + additionalProperties: true + type: object + required: + - computed_at + - jobs_by_state + - by_environment + - live_sends + - needs_you + - window + - totals + - money_live_micro + title: AdminStats + AdminWhoami: + properties: + kind: + type: string + enum: + - user + - service + - dev + title: Kind + identity: + type: string + title: Identity + actor: + type: string + title: Actor + read_only: type: boolean - title: Payment Method Saved - setup_session_id: + title: Read Only + additionalProperties: true + type: object + required: + - kind + - identity + - actor + - read_only + title: AdminWhoami + Agent: + properties: + id: + type: string + title: Id + name: + type: string + title: Name + purpose: anyOf: - type: string - type: 'null' - title: Setup Session Id - setup_url: + title: Purpose + enabled: + type: boolean + title: Enabled + status: + type: string + title: Status + project_id: anyOf: - type: string - type: 'null' - title: Setup Url - description: Where to save the card while status is pending_setup. + title: Project Id + policy_ids: + items: + type: string + type: array + title: Policy Ids + contact_email: + anyOf: + - type: string + - type: 'null' + title: Contact Email + budget_id: + anyOf: + - type: string + - type: 'null' + title: Budget Id + budgets: + items: + additionalProperties: true + type: object + type: array + title: Budgets + keys: + items: + $ref: '#/components/schemas/ApiKey' + type: array + title: Keys + created_by: + type: string + title: Created By created_at: type: string title: Created At updated_at: type: string title: Updated At - last_attempt_at: - anyOf: - - type: string - - type: 'null' - title: Last Attempt At disabled_at: anyOf: - type: string @@ -8910,933 +9749,1035 @@ components: type: object required: - id - - environment + - name + - purpose + - enabled - status - - threshold_micro - - amount_micro - - daily_cap_micro - - payment_method_saved - - setup_session_id + - project_id + - policy_ids + - contact_email + - budget_id + - budgets + - keys + - created_by - created_at - updated_at - - last_attempt_at - disabled_at - title: AutoTopup - AutoTopupCreate: + title: Agent + AgentActivity: properties: - threshold_micro: - type: integer - minimum: 0.0 - title: Threshold Micro - description: Top up when the available balance drops below this. - amount_micro: - type: integer - maximum: 1000000000.0 - minimum: 10000000.0 - title: Amount Micro - description: Whole dollars, 10 to 1000 USD. - daily_cap_micro: - type: integer - minimum: 10000000.0 - title: Daily Cap Micro - description: Never more than this per UTC day. - additionalProperties: false + id: + type: string + title: Id + actor: + type: string + title: Actor + action: + type: string + title: Action + resource_type: + type: string + title: Resource Type + resource_id: + anyOf: + - type: string + - type: 'null' + title: Resource Id + data: + additionalProperties: true + type: object + title: Data + correlation_id: + anyOf: + - type: string + - type: 'null' + title: Correlation Id + created_at: + type: string + title: Created At + additionalProperties: true type: object required: - - threshold_micro - - amount_micro - - daily_cap_micro - title: AutoTopupCreate - AutoTopupList: + - id + - actor + - action + - resource_type + - resource_id + - data + - correlation_id + - created_at + title: AgentActivity + AgentActivityPage: properties: items: items: - $ref: '#/components/schemas/AutoTopup' + $ref: '#/components/schemas/AgentActivity' type: array title: Items + next_cursor: + anyOf: + - type: string + - type: 'null' + title: Next Cursor additionalProperties: true type: object required: - items - title: AutoTopupList - Balance: + title: AgentActivityPage + AgentCreate: properties: - environment: - type: string - enum: - - live - - test - title: Environment - currency: + name: type: string - title: Currency - available_micro: - type: integer - title: Available Micro - held_micro: - type: integer - title: Held Micro - additionalProperties: true - type: object - required: - - environment - - currency - - available_micro - - held_micro - title: Balance - BillingSettings: - properties: - org_id: - type: string - title: Org Id - billing_email: + maxLength: 120 + minLength: 1 + title: Name + purpose: anyOf: - type: string + maxLength: 500 - type: 'null' - title: Billing Email - low_balance_threshold_micro: - type: integer - title: Low Balance Threshold Micro - low_balance_active: - type: boolean - title: Low Balance Active - workspace: - type: boolean - title: Workspace - disputed: - type: boolean - title: Disputed - stripe_customer: - type: boolean - title: Stripe Customer - additionalProperties: true - type: object - required: - - org_id - - billing_email - - low_balance_threshold_micro - - low_balance_active - - workspace - - disputed - - stripe_customer - title: BillingSettings - BillingSettingsPatch: - properties: - billing_email: + title: Purpose + description: Shown to humans, never evaluated. + policy_ids: + items: + type: string + type: array + title: Policy Ids + contact_email: anyOf: - type: string maxLength: 254 - format: email - - type: 'null' - title: Billing Email - low_balance_threshold_micro: - anyOf: - - type: integer - minimum: 0.0 - type: 'null' - title: Low Balance Threshold Micro + title: Contact Email + description: Where decided approvals are mailed. additionalProperties: false type: object - title: BillingSettingsPatch - required: [] - Budget: + required: + - name + title: AgentCreate + AgentKeyCreate: properties: - id: + label: type: string - title: Id + maxLength: 120 + minLength: 1 + title: Label + default: agent key environment: type: string enum: - live - test title: Environment - scope: - type: string - enum: - - organization - - project - - agent - title: Scope - project_id: - anyOf: - - type: string - - type: 'null' - title: Project Id - agent_id: - anyOf: - - type: string - - type: 'null' - title: Agent Id - period: - type: string - enum: - - daily - - monthly - title: Period - limit_micro: - type: integer - title: Limit Micro - spent_micro: - type: integer - title: Spent Micro - remaining_micro: - type: integer - title: Remaining Micro - status: - type: string - enum: - - active - - disabled - title: Status - resets_at: - type: string - title: Resets At - created_at: - type: string - title: Created At - updated_at: - type: string - title: Updated At - additionalProperties: true - type: object - required: - - id - - environment - - scope - - project_id - - agent_id - - period - - limit_micro - - spent_micro - - remaining_micro - - status - - resets_at - - created_at - - updated_at - title: Budget - BudgetCreate: - properties: - scope: - type: string - enum: - - organization - - project - - agent - title: Scope - default: organization - project_id: + default: test + scopes: anyOf: - - type: string + - items: + type: string + type: array - type: 'null' - title: Project Id - agent_id: + title: Scopes + description: Omit for the default agent set. Never approval:decide, key:admin, policy:admin, agent:admin. + expires_in_days: anyOf: - - type: string + - type: integer + maximum: 3650.0 + minimum: 1.0 - type: 'null' - title: Agent Id - period: - type: string - enum: - - daily - - monthly - title: Period - limit_micro: - type: integer - minimum: 0.0 - title: Limit Micro + title: Expires In Days additionalProperties: false type: object - required: - - period - - limit_micro - title: BudgetCreate - BudgetList: + title: AgentKeyCreate + AgentList: properties: items: items: - $ref: '#/components/schemas/Budget' + $ref: '#/components/schemas/Agent' type: array title: Items additionalProperties: true type: object required: - items - title: BudgetList - BudgetPatch: + title: AgentList + AgentUpdate: properties: - limit_micro: + name: anyOf: - - type: integer - minimum: 0.0 + - type: string + maxLength: 120 + minLength: 1 - type: 'null' - title: Limit Micro - status: + title: Name + purpose: anyOf: - type: string - enum: - - active - - disabled + maxLength: 500 - type: 'null' - title: Status + title: Purpose + enabled: + anyOf: + - type: boolean + - type: 'null' + title: Enabled + description: false revokes every key of the agent. + policy_ids: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Policy Ids + contact_email: + anyOf: + - type: string + maxLength: 254 + - type: 'null' + title: Contact Email additionalProperties: false type: object - title: BudgetPatch - required: [] - Capabilities: + title: AgentUpdate + AnalyticsJourneys: properties: - environment: - type: string - enum: - - live - - test - title: Environment - provider: + computed_at: type: string - title: Provider - send: - type: boolean - title: Send - receive: - type: boolean - title: Receive - numbers: - type: boolean - title: Numbers - routes: - items: + title: Computed At + days: + type: integer + title: Days + window: + additionalProperties: type: string - type: array - title: Routes - formats: + type: object + title: Window + cohort: + type: integer + title: Cohort + funnel: items: - type: string + additionalProperties: true + type: object type: array - title: Formats - max_pages: - type: integer - title: Max Pages - max_upload_bytes: + title: Funnel + leakiest: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Leakiest + doors: + items: + additionalProperties: true + type: object + type: array + title: Doors + topups: + additionalProperties: true + type: object + title: Topups + site: + additionalProperties: true + type: object + title: Site + organizations: + items: + additionalProperties: true + type: object + type: array + title: Organizations + listed: type: integer - title: Max Upload Bytes - price_version: + title: Listed + additionalProperties: true + type: object + required: + - computed_at + - days + - window + - cohort + - funnel + - doors + - topups + - site + - organizations + - listed + title: AnalyticsJourneys + AnalyticsOrganization: + properties: + computed_at: type: string - title: Price Version - rates: + title: Computed At + days: + type: integer + title: Days + window: + additionalProperties: + type: string + type: object + title: Window + organization: additionalProperties: true type: object - title: Rates - scopes: + title: Organization + steps: items: - type: string + additionalProperties: true + type: object type: array - title: Scopes - approvals: - type: boolean - title: Approvals - extraction: - type: boolean - title: Extraction - limits: - additionalProperties: - type: integer + title: Steps + members: + items: + additionalProperties: true + type: object + type: array + title: Members + doors: + items: + additionalProperties: true + type: object + type: array + title: Doors + keys: + additionalProperties: true type: object - title: Limits - one_time: + title: Keys + jobs: additionalProperties: true type: object - title: One Time + title: Jobs + topups: + items: + additionalProperties: true + type: object + type: array + title: Topups + inbound: + additionalProperties: true + type: object + title: Inbound + numbers: + additionalProperties: true + type: object + title: Numbers + actions: + items: + additionalProperties: true + type: object + type: array + title: Actions + recent: + items: + additionalProperties: true + type: object + type: array + title: Recent + usage: + items: + additionalProperties: true + type: object + type: array + title: Usage + tools: + items: + additionalProperties: true + type: object + type: array + title: Tools additionalProperties: true type: object required: - - environment - - provider - - send - - receive + - computed_at + - days + - window + - organization + - steps + - members + - doors + - keys + - jobs + - topups + - inbound - numbers - - routes - - formats - - max_pages - - max_upload_bytes - - price_version - - rates - - scopes - - approvals - - limits - title: Capabilities - ClassificationCreate: + - actions + - recent + - usage + - tools + title: AnalyticsOrganization + AnalyticsSource: properties: - inbox_item_id: + source: + type: string + title: Source + label: + type: string + title: Label + status: + type: string + title: Status + missing: + items: + type: string + type: array + title: Missing + step: + type: integer + title: Step + error: anyOf: - type: string - pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - type: 'null' - title: Inbox Item Id - document_id: + title: Error + last_pulled_at: anyOf: - type: string - pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - type: 'null' - title: Document Id - max_pages: + title: Last Pulled At + latest_day: anyOf: - - type: integer - maximum: 200.0 - minimum: 1.0 + - type: string - type: 'null' - title: Max Pages - strict: - type: boolean - title: Strict - description: Refuse with page_limit_exceeded when the document has more pages than would be read, instead - of reading the first ones and warning. - default: false - document_types: - items: - type: string - type: array - maxItems: 30 - minItems: 1 - title: Document Types - abstain_below: - type: number - maximum: 1.0 - minimum: 0.0 - title: Abstain Below - default: 0.6 - strong_model_if_below: - type: number - maximum: 1.0 - minimum: 0.0 - title: Strong Model If Below - default: 0.75 - additionalProperties: false + title: Latest Day + additionalProperties: true type: object required: - - document_types - title: ClassificationCreate - Correction: + - source + - label + - status + - missing + - step + title: AnalyticsSource + AnalyticsSources: properties: - id: - type: string - title: Id - extraction_id: - type: string - title: Extraction Id - field: - type: string - title: Field - value: - title: Value - note: - anyOf: - - type: string - - type: 'null' - title: Note - created_by: - type: string - title: Created By - created_at: - type: string - title: Created At + items: + items: + $ref: '#/components/schemas/AnalyticsSource' + type: array + title: Items + pull: + additionalProperties: true + type: object + title: Pull additionalProperties: true type: object required: - - id - - extraction_id - - field - - value - - note - - created_by - - created_at - title: Correction - CorrectionCreate: - properties: - field: - anyOf: - - type: string - maxLength: 120 - - type: 'null' - title: Field - value: - title: Value - note: - anyOf: - - type: string - maxLength: 1000 - - type: 'null' - title: Note - additionalProperties: false - type: object - title: CorrectionCreate - required: [] - Coverage: + - items + - pull + title: AnalyticsSources + AnalyticsUsage: properties: - coverage_version: - anyOf: - - type: string - - type: 'null' - title: Coverage Version - generated_at: + computed_at: + type: string + title: Computed At + days: + type: integer + title: Days + window: + additionalProperties: + type: string + type: object + title: Window + org_id: anyOf: - type: string - type: 'null' - title: Generated At - orderable_now: - items: - type: string - type: array - title: Orderable Now - outbound: + title: Org Id + totals: additionalProperties: true type: object - title: Outbound - inbound: + title: Totals + by_day: + items: + additionalProperties: true + type: object + type: array + title: By Day + by_surface: + items: + additionalProperties: true + type: object + type: array + title: By Surface + by_family: + items: + additionalProperties: true + type: object + type: array + title: By Family + top_organizations: + items: + additionalProperties: true + type: object + type: array + title: Top Organizations + top_keys: + items: + additionalProperties: true + type: object + type: array + title: Top Keys + tools: additionalProperties: true type: object - title: Inbound - excluded: + title: Tools + data_age: additionalProperties: true type: object - title: Excluded - note: - anyOf: - - type: string - - type: 'null' - title: Note + title: Data Age additionalProperties: true type: object required: - - coverage_version - - generated_at - - orderable_now - - outbound - - inbound - - excluded - - note - title: Coverage - Deleted: + - computed_at + - days + - window + - totals + - by_day + - by_surface + - by_family + - top_organizations + - top_keys + - tools + - data_age + title: AnalyticsUsage + AnalyticsWebsite: properties: - id: + computed_at: type: string - title: Id - deleted: - type: boolean - title: Deleted - default: true + title: Computed At + days: + type: integer + title: Days + window: + additionalProperties: + type: string + type: object + title: Window + sources: + items: + $ref: '#/components/schemas/AnalyticsSource' + type: array + title: Sources + search: + additionalProperties: true + type: object + title: Search + traffic: + additionalProperties: true + type: object + title: Traffic + site: + additionalProperties: true + type: object + title: Site + ux: + additionalProperties: true + type: object + title: Ux + bing: + additionalProperties: true + type: object + title: Bing + authority: + additionalProperties: true + type: object + title: Authority + data_age: + additionalProperties: true + type: object + title: Data Age additionalProperties: true type: object required: - - id - title: Deleted - Delivery: + - computed_at + - days + - window + - sources + - search + - traffic + - site + - ux + - bing + - authority + - data_age + title: AnalyticsWebsite + ApiKey: properties: id: type: string title: Id - webhook_id: - type: string - title: Webhook Id - event_id: + label: type: string - title: Event Id - event_type: + title: Label + prefix: type: string - title: Event Type - attempt: - type: integer - title: Attempt - status: + title: Prefix + environment: type: string - title: Status - response_status: - anyOf: - - type: integer - - type: 'null' - title: Response Status - error: + enum: + - live + - test + title: Environment + project_id: anyOf: - type: string - type: 'null' - title: Error - duration_ms: + title: Project Id + agent_id: anyOf: - - type: integer + - type: string - type: 'null' - title: Duration Ms - replay_of: + title: Agent Id + scopes: + items: + type: string + type: array + title: Scopes + created_at: + type: string + title: Created At + last_used_at: anyOf: - type: string - type: 'null' - title: Replay Of - schedule_s: - items: - type: integer - type: array - title: Schedule S - next_attempt_at: + title: Last Used At + expires_at: anyOf: - type: string - type: 'null' - title: Next Attempt At - created_at: - type: string - title: Created At - delivered_at: + title: Expires At + revoked_at: anyOf: - type: string - type: 'null' - title: Delivered At + title: Revoked At + rotated_from_id: + anyOf: + - type: string + - type: 'null' + title: Rotated From Id + connected_app: + type: boolean + title: Connected App + description: The key belongs to a connected app used over stdio, such as the Gemini CLI extension. The + organization's auto-send setting (Settings, Connected apps) binds it like an OAuth connection. + default: false additionalProperties: true type: object required: - id - - webhook_id - - event_id - - event_type - - attempt - - status - - response_status - - error - - duration_ms - - replay_of - - schedule_s - - next_attempt_at + - label + - prefix + - environment + - project_id + - agent_id + - scopes - created_at - - delivered_at - title: Delivery - DeliveryPage: + - last_used_at + - expires_at + - revoked_at + - rotated_from_id + title: ApiKey + ApiKeyCreate: + properties: + label: + type: string + maxLength: 120 + minLength: 1 + title: Label + environment: + type: string + enum: + - live + - test + title: Environment + default: test + scopes: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Scopes + description: Omit for the default agent set; '*' for all. + expires_in_days: + anyOf: + - type: integer + maximum: 3650.0 + minimum: 1.0 + - type: 'null' + title: Expires In Days + connected_app: + type: boolean + title: Connected App + description: 'This key is for a connected app such as the Gemini CLI extension: the auto-send setting + binds it. Not available on an agent''s key.' + default: false + additionalProperties: false + type: object + required: + - label + title: ApiKeyCreate + ApiKeyList: properties: items: items: - $ref: '#/components/schemas/Delivery' + $ref: '#/components/schemas/ApiKey' type: array title: Items - next_cursor: - anyOf: - - type: string - - type: 'null' - title: Next Cursor additionalProperties: true type: object required: - items - title: DeliveryPage - Document: + title: ApiKeyList + ApiKeyWithSecret: properties: id: type: string title: Id + label: + type: string + title: Label + prefix: + type: string + title: Prefix environment: type: string enum: - live - test title: Environment - status: - type: string - title: Status - source: - type: string - title: Source - filename: - type: string - title: Filename - content_type: - type: string - title: Content Type - size_bytes: - type: integer - title: Size Bytes - sha256: - type: string - title: Sha256 - page_count: - type: integer - title: Page Count - render_status: - type: string - title: Render Status - render_reason: + project_id: anyOf: - type: string - type: 'null' - title: Render Reason - validation: - additionalProperties: true - type: object - title: Validation - upload_id: + title: Project Id + agent_id: anyOf: - type: string - type: 'null' - title: Upload Id + title: Agent Id + scopes: + items: + type: string + type: array + title: Scopes created_at: type: string title: Created At + last_used_at: + anyOf: + - type: string + - type: 'null' + title: Last Used At expires_at: anyOf: - type: string - type: 'null' title: Expires At - deleted_at: + revoked_at: anyOf: - type: string - type: 'null' - title: Deleted At + title: Revoked At + rotated_from_id: + anyOf: + - type: string + - type: 'null' + title: Rotated From Id + connected_app: + type: boolean + title: Connected App + description: The key belongs to a connected app used over stdio, such as the Gemini CLI extension. The + organization's auto-send setting (Settings, Connected apps) binds it like an OAuth connection. + default: false + secret: + type: string + title: Secret + description: Shown once. Store it now; it cannot be read back. additionalProperties: true type: object required: - id + - label + - prefix - environment - - status - - source - - filename - - content_type - - size_bytes - - sha256 - - page_count - - render_status - - render_reason - - validation - - upload_id + - project_id + - agent_id + - scopes - created_at + - last_used_at - expires_at - - deleted_at - title: Document - DocumentCreate: + - revoked_at + - rotated_from_id + - secret + title: ApiKeyWithSecret + ApprovalDecision: properties: - upload_id: + reason: anyOf: - type: string + maxLength: 500 - type: 'null' - title: Upload Id - text: + title: Reason + additionalProperties: false + type: object + title: ApprovalDecision + ApprovalRequest: + properties: + id: + type: string + title: Id + kind: + type: string + enum: + - send + - help + title: Kind + status: + type: string + enum: + - pending + - approved + - rejected + - revoked + - expired + title: Status + quote_id: anyOf: - type: string - type: 'null' - title: Text - description: Plain text rendered to a letter PDF. - examples: - - Hello from Fax for Agents - markdown: + title: Quote Id + bound: + additionalProperties: true + type: object + title: Bound + message: anyOf: - type: string - type: 'null' - title: Markdown - description: Markdown (headings, lists, emphasis, code) rendered to PDF. - html: + title: Message + requested_by: + type: string + title: Requested By + decided_by: anyOf: - type: string - type: 'null' - title: Html - description: 'HTML rendered to PDF by a headless browser with scripts off and no network: inline CSS and - data: images print, external resources are counted as blocked. US letter unless @page says otherwise. - At most 2 MiB.' - filename: + title: Decided By + reason: anyOf: - type: string - maxLength: 255 - type: 'null' - title: Filename - additionalProperties: false - type: object - title: DocumentCreate - required: [] - description: Send exactly one of text, markdown, html or upload_id. - DocumentPage: - properties: - items: - items: - $ref: '#/components/schemas/Document' - type: array - title: Items - next_cursor: + title: Reason + consumed_by_job_id: anyOf: - type: string - type: 'null' - title: Next Cursor + title: Consumed By Job Id + created_at: + type: string + title: Created At + expires_at: + anyOf: + - type: string + - type: 'null' + title: Expires At + decided_at: + anyOf: + - type: string + - type: 'null' + title: Decided At + notified: + anyOf: + - type: boolean + - type: 'null' + title: Notified + description: 'On creation: whether the organization was mailed. false when the request repeats a pending + one or the organization''s daily cap of request mails is reached; the request is still shown in the + dashboard.' additionalProperties: true type: object required: - - items - title: DocumentPage - DocumentPages: + - id + - kind + - status + - quote_id + - bound + - message + - requested_by + - decided_by + - reason + - consumed_by_job_id + - created_at + - expires_at + - decided_at + title: ApprovalRequest + ApprovalRequestCreate: properties: - document_id: - type: string - title: Document Id - page_count: - type: integer - title: Page Count - render_status: + kind: type: string - title: Render Status - render_reason: + enum: + - send + - help + title: Kind + default: send + quote_id: anyOf: - type: string + pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - type: 'null' - title: Render Reason + title: Quote Id + description: The quote to bind (kind send). + message: + anyOf: + - type: string + maxLength: 2000 + - type: 'null' + title: Message + description: What the person should know (kind help). Shown as data. + fax_job_id: + anyOf: + - type: string + pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ + - type: 'null' + title: Fax Job Id + additionalProperties: false + type: object + title: ApprovalRequestCreate + ApprovalRequestPage: + properties: items: items: - $ref: '#/components/schemas/PagePreview' + $ref: '#/components/schemas/ApprovalRequest' type: array title: Items + next_cursor: + anyOf: + - type: string + - type: 'null' + title: Next Cursor additionalProperties: true type: object required: - - document_id - - page_count - - render_status - - render_reason - items - title: DocumentPages - DocumentValidation: + title: ApprovalRequestPage + Attempt: properties: id: type: string title: Id + fax_job_id: + type: string + title: Fax Job Id + attempt_number: + type: integer + title: Attempt Number + provider: + type: string + title: Provider + provider_ref: + anyOf: + - type: string + - type: 'null' + title: Provider Ref status: type: string title: Status - page_count: + pages_sent: type: integer - title: Page Count - render_status: - type: string - title: Render Status - render_reason: + title: Pages Sent + failure_code: anyOf: - type: string - type: 'null' - title: Render Reason - warnings: - items: - additionalProperties: true + title: Failure Code + evidence: + anyOf: + - additionalProperties: true type: object - type: array - title: Warnings + - type: 'null' + title: Evidence + error: + anyOf: + - type: string + - type: 'null' + title: Error + started_at: + type: string + title: Started At + finished_at: + anyOf: + - type: string + - type: 'null' + title: Finished At additionalProperties: true type: object required: - id + - fax_job_id + - attempt_number + - provider + - provider_ref - status - - page_count - - render_status - - render_reason - - warnings - title: DocumentValidation - EffectivePolicies: + - pages_sent + - failure_code + - evidence + - error + - started_at + - finished_at + title: Attempt + AttemptPage: properties: - actor: - type: string - title: Actor - environment: - type: string - enum: - - live - - test - title: Environment - under_policy: - type: boolean - title: Under Policy - approval_required_without_policy: - type: boolean - title: Approval Required Without Policy - policies: - items: - $ref: '#/components/schemas/Policy' - type: array - title: Policies - rules_order: + items: items: - type: string + $ref: '#/components/schemas/Attempt' type: array - title: Rules Order - requires_human_approval: - type: boolean - title: Requires Human Approval - description: 'Server-set for hosted integration clients and their keys: every send needs a person''s approval, - whatever the policies say.' - default: false + title: Items additionalProperties: true type: object required: - - actor - - environment - - under_policy - - approval_required_without_policy - - policies - - rules_order - title: EffectivePolicies - Event: + - items + title: AttemptPage + AuditEvent: properties: id: type: string title: Id - type: - type: string - title: Type - created_at: - type: string - title: Created At org_id: - type: string - title: Org Id - environment: - type: string - enum: - - live - - test - title: Environment - project_id: anyOf: - type: string - type: 'null' - title: Project Id + title: Org Id + actor: + type: string + title: Actor + action: + type: string + title: Action resource_type: type: string title: Resource Type resource_id: - type: string - title: Resource Id - fax_job_id: - anyOf: - - type: string - - type: 'null' - title: Fax Job Id - schema_version: - type: integer - title: Schema Version - correlation_id: anyOf: - type: string - type: 'null' - title: Correlation Id - data: - additionalProperties: true - type: object - title: Data + title: Resource Id + created_at: + type: string + title: Created At additionalProperties: true type: object required: - id - - type - - created_at - - org_id - - environment - - project_id + - actor + - action - resource_type - - resource_id - - fax_job_id - - schema_version - - correlation_id - - data - title: Event - EventPage: + - created_at + title: AuditEvent + AuditEventPage: properties: items: items: - $ref: '#/components/schemas/Event' + $ref: '#/components/schemas/AuditEvent' type: array title: Items next_cursor: @@ -9848,366 +10789,265 @@ components: type: object required: - items - title: EventPage - Extraction: + title: AuditEventPage + AutoSendSetting: properties: - id: - type: string - title: Id - kind: - type: string - enum: - - extract - - classify - - summarize - title: Kind - status: - type: string - enum: - - pending - - queued - - running - - completed - - failed - title: Status environment: type: string enum: - live - test title: Environment - document_id: - type: string - title: Document Id - inbox_item_id: - anyOf: - - type: string - - type: 'null' - title: Inbox Item Id - schema_id: + enabled: + type: boolean + title: Enabled + max_amount_micro: + type: integer + title: Max Amount Micro + description: Amount per fax, at most, in micro-units of USD. + daily_max_jobs: + type: integer + title: Daily Max Jobs + description: Faxes per UTC day, at most, across every connected app. + policy_id: anyOf: - type: string - type: 'null' - title: Schema Id - schema: + title: Policy Id + description: The managed policy; null before the first enable. + version: anyOf: - - additionalProperties: true - type: object + - type: integer - type: 'null' - title: Schema - options: - additionalProperties: true - type: object - title: Options - pages_requested: + title: Version + additionalProperties: true + type: object + required: + - environment + - enabled + - max_amount_micro + - daily_max_jobs + - policy_id + - version + title: AutoSendSetting + description: |- + The organization's auto-send setting for one environment: Settings, Connected apps, "Let connected + apps send without asking". Stored as the managed policy ``policy_id`` (docs/APPROVALS.md). + AutoSendUpdate: + properties: + enabled: + type: boolean + title: Enabled + max_amount_micro: type: integer - title: Pages Requested - pages_processed: + maximum: 1000000000000.0 + minimum: 0.0 + title: Max Amount Micro + description: Amount per fax, at most, in micro-units of USD (5000000 is five US dollars). + default: 5000000 + daily_max_jobs: type: integer - title: Pages Processed - pages_escalated: + maximum: 100000.0 + minimum: 1.0 + title: Daily Max Jobs + description: Faxes per UTC day. + default: 20 + additionalProperties: false + type: object + required: + - enabled + title: AutoSendUpdate + AutoTopup: + properties: + id: + type: string + title: Id + environment: + type: string + enum: + - live + - test + title: Environment + status: + type: string + enum: + - pending_setup + - active + - disabled + title: Status + threshold_micro: type: integer - title: Pages Escalated - document_pages: + title: Threshold Micro + amount_micro: type: integer - title: Document Pages - description: The document's page count. Above pages_processed on a completed row means the cap left pages - unread; output.truncated and a warning say so. - confidence: - anyOf: - - type: number - - type: 'null' - title: Confidence - pages: - items: - $ref: '#/components/schemas/ExtractionPage' - type: array - title: Pages - output: + title: Amount Micro + daily_cap_micro: + type: integer + title: Daily Cap Micro + payment_method_saved: + type: boolean + title: Payment Method Saved + setup_session_id: anyOf: - - additionalProperties: true - type: object + - type: string - type: 'null' - title: Output - models: - additionalProperties: true - type: object - title: Models - price_micro: - type: integer - title: Price Micro - provider_cost_micro: - type: integer - title: Provider Cost Micro - currency: - type: string - title: Currency - error_code: + title: Setup Session Id + setup_url: anyOf: - type: string - type: 'null' - title: Error Code - created_by: - type: string - title: Created By + title: Setup Url + description: Where to save the card while status is pending_setup. created_at: type: string title: Created At - started_at: + updated_at: + type: string + title: Updated At + last_attempt_at: anyOf: - type: string - type: 'null' - title: Started At - completed_at: + title: Last Attempt At + disabled_at: anyOf: - type: string - type: 'null' - title: Completed At - corrections: - anyOf: - - items: - additionalProperties: true - type: object - type: array - - type: 'null' - title: Corrections + title: Disabled At additionalProperties: true type: object required: - id - - kind - - status - environment - - document_id - - inbox_item_id - - schema_id - - options - - pages_requested - - pages_processed - - pages_escalated - - document_pages - - confidence - - pages - - output - - models - - price_micro - - provider_cost_micro - - currency - - error_code - - created_by + - status + - threshold_micro + - amount_micro + - daily_cap_micro + - payment_method_saved + - setup_session_id - created_at - - started_at - - completed_at - title: Extraction - ExtractionCreate: - properties: - inbox_item_id: - anyOf: - - type: string - pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - - type: 'null' - title: Inbox Item Id - document_id: - anyOf: - - type: string - pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - - type: 'null' - title: Document Id - max_pages: - anyOf: - - type: integer - maximum: 200.0 - minimum: 1.0 - - type: 'null' - title: Max Pages - strict: - type: boolean - title: Strict - description: Refuse with page_limit_exceeded when the document has more pages than would be read, instead - of reading the first ones and warning. - default: false - schema: - anyOf: - - additionalProperties: true - type: object - - type: 'null' - title: Schema - description: A JSON Schema object of string, number, boolean or date fields with descriptions. - schema_id: - anyOf: - - type: string - pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - - type: 'null' - title: Schema Id - strong_model_if_below: - type: number - maximum: 1.0 - minimum: 0.0 - title: Strong Model If Below - default: 0.75 - additionalProperties: false - type: object - title: ExtractionCreate - required: [] - ExtractionPage: + - updated_at + - last_attempt_at + - disabled_at + title: AutoTopup + AutoTopupCreate: properties: - page: + threshold_micro: type: integer - title: Page - text: - type: string - title: Text - confidence: - type: number - title: Confidence - language: - anyOf: - - type: string - - type: 'null' - title: Language - status: - type: string - title: Status - model: - type: string - title: Model - escalated: - type: boolean - title: Escalated - default: false - warnings: - items: - type: string - type: array - title: Warnings - default: [] - fast_read: - type: boolean - description: Returned by GET /v1/extractions/{extraction_id}. Declared here until the public schema includes - it. - x-sdk-route: GET /v1/extractions/{extraction_id} - strong_read: - type: boolean - description: Returned by GET /v1/extractions/{extraction_id}. Declared here until the public schema includes - it. - x-sdk-route: GET /v1/extractions/{extraction_id} - cost_micro: + minimum: 0.0 + title: Threshold Micro + description: Top up when the available balance drops below this. + amount_micro: type: integer - description: Returned by GET /v1/extractions/{extraction_id}. Declared here until the public schema includes - it. - x-sdk-route: GET /v1/extractions/{extraction_id} - words_count: + maximum: 1000000000.0 + minimum: 10000000.0 + title: Amount Micro + description: Whole dollars, 10 to 1000 USD. + daily_cap_micro: type: integer - description: Returned by GET /v1/extractions/{extraction_id}. Declared here until the public schema includes - it. - x-sdk-route: GET /v1/extractions/{extraction_id} - additionalProperties: true + minimum: 10000000.0 + title: Daily Cap Micro + description: Never more than this per UTC day. + additionalProperties: false type: object required: - - page - - text - - confidence - - status - - model - title: ExtractionPage - ExtractionPageList: + - threshold_micro + - amount_micro + - daily_cap_micro + title: AutoTopupCreate + AutoTopupList: properties: items: items: - $ref: '#/components/schemas/Extraction' + $ref: '#/components/schemas/AutoTopup' type: array title: Items - next_cursor: - anyOf: - - type: string - - type: 'null' - title: Next Cursor additionalProperties: true type: object required: - items - title: ExtractionPageList - ExtractionSchema: + title: AutoTopupList + Balance: properties: - id: + environment: type: string - title: Id - name: + enum: + - live + - test + title: Environment + currency: type: string - title: Name - version: + title: Currency + available_micro: type: integer - title: Version - description: - anyOf: - - type: string - - type: 'null' - title: Description - schema: - additionalProperties: true - type: object - title: Schema - status: - type: string - title: Status - created_by: - type: string - title: Created By - created_at: - type: string - title: Created At + title: Available Micro + held_micro: + type: integer + title: Held Micro additionalProperties: true type: object required: - - id - - name - - version - - description - - schema - - status - - created_by - - created_at - title: ExtractionSchema - ExtractionSchemaList: + - environment + - currency + - available_micro + - held_micro + title: Balance + BillingSettings: properties: - items: - items: - $ref: '#/components/schemas/ExtractionSchema' - type: array - title: Items + org_id: + type: string + title: Org Id + billing_email: + anyOf: + - type: string + - type: 'null' + title: Billing Email + low_balance_threshold_micro: + type: integer + title: Low Balance Threshold Micro + low_balance_active: + type: boolean + title: Low Balance Active + workspace: + type: boolean + title: Workspace + disputed: + type: boolean + title: Disputed + stripe_customer: + type: boolean + title: Stripe Customer additionalProperties: true type: object required: - - items - title: ExtractionSchemaList - FaxCreate: + - org_id + - billing_email + - low_balance_threshold_micro + - low_balance_active + - workspace + - disputed + - stripe_customer + title: BillingSettings + BillingSettingsPatch: properties: - quote_id: - type: string - title: Quote Id - client_reference: + billing_email: anyOf: - type: string - maxLength: 200 + maxLength: 254 + format: email - type: 'null' - title: Client Reference - approval_id: + title: Billing Email + low_balance_threshold_micro: anyOf: - - type: string - pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ + - type: integer + minimum: 0.0 - type: 'null' - title: Approval Id - description: An approved approval request bound to this quote. Consumed by this one send. + title: Low Balance Threshold Micro additionalProperties: false type: object - required: - - quote_id - title: FaxCreate - description: |- - M9: an approved request authorizes the send; without one a quote that needs authorization is - evaluated against the caller's delegated policies. - FaxJob: - properties: + title: BillingSettingsPatch + Budget: + properties: id: type: string title: Id @@ -10217,740 +11057,677 @@ components: - live - test title: Environment - direction: - type: string - title: Direction - state: + scope: type: string enum: - - queued - - active - - partial - - failed - - unknown - - delivered - title: State - failure_code: - anyOf: - - type: string - - type: 'null' - title: Failure Code - to: - type: string - title: To - from: - anyOf: - - type: string - - type: 'null' - title: From - route: - type: string - title: Route - document_id: - type: string - title: Document Id - document_sha256: - type: string - title: Document Sha256 - transmitted_sha256: + - organization + - project + - agent + title: Scope + project_id: anyOf: - type: string - type: 'null' - title: Transmitted Sha256 - quote_id: + title: Project Id + agent_id: anyOf: - type: string - type: 'null' - title: Quote Id - billable_pages: - type: integer - title: Billable Pages - cover_pages: - type: integer - title: Cover Pages - quality: + title: Agent Id + period: type: string - title: Quality - default: fine - sender_name: - anyOf: - - type: string - - type: 'null' - title: Sender Name - delete_after_send: - type: boolean - title: Delete After Send - default: false - pages_sent: + enum: + - daily + - monthly + title: Period + limit_micro: type: integer - title: Pages Sent - amount_micro: + title: Limit Micro + spent_micro: type: integer - title: Amount Micro - captured_micro: + title: Spent Micro + remaining_micro: type: integer - title: Captured Micro - currency: + title: Remaining Micro + status: type: string - title: Currency - provider: + enum: + - active + - disabled + title: Status + resets_at: type: string - title: Provider - provider_ref: - anyOf: - - type: string - - type: 'null' - title: Provider Ref - client_reference: - anyOf: - - type: string - - type: 'null' - title: Client Reference - attempt_count: - type: integer - title: Attempt Count + title: Resets At created_at: type: string title: Created At updated_at: type: string title: Updated At - completed_at: - anyOf: - - type: string - - type: 'null' - title: Completed At - cancel_requested_at: - anyOf: - - type: string - - type: 'null' - title: Cancel Requested At - evidence: - anyOf: - - additionalProperties: true - type: object - - type: 'null' - title: Evidence - description: The carrier's evidence once delivered or partial; null before. - approval_id: - type: - - string - - 'null' - description: Returned by GET /v1/faxes/{fax_id}. Declared here until the public schema includes it. - x-sdk-route: GET /v1/faxes/{fax_id} - policy_version_id: - type: - - string - - 'null' - description: Returned by GET /v1/faxes/{fax_id}. Declared here until the public schema includes it. - x-sdk-route: GET /v1/faxes/{fax_id} - one_time_order_id: - type: - - string - - 'null' - description: Returned by GET /v1/faxes/{fax_id}. Declared here until the public schema includes it. - x-sdk-route: GET /v1/faxes/{fax_id} - one_time: - type: - - object - - 'null' - description: Returned by GET /v1/faxes/{fax_id}. Declared here until the public schema includes it. - x-sdk-route: GET /v1/faxes/{fax_id} - additionalProperties: true additionalProperties: true type: object required: - id - environment - - direction - - state - - failure_code - - to - - route - - document_id - - document_sha256 - - transmitted_sha256 - - quote_id - - billable_pages - - cover_pages - - pages_sent - - amount_micro - - captured_micro - - currency - - provider - - provider_ref - - client_reference - - attempt_count + - scope + - project_id + - agent_id + - period + - limit_micro + - spent_micro + - remaining_micro + - status + - resets_at - created_at - updated_at - - completed_at - - cancel_requested_at - title: FaxJob - FaxJobPage: + title: Budget + BudgetCreate: properties: - items: - items: - $ref: '#/components/schemas/FaxJob' - type: array - title: Items - next_cursor: + scope: + type: string + enum: + - organization + - project + - agent + title: Scope + default: organization + project_id: anyOf: - type: string - type: 'null' - title: Next Cursor - additionalProperties: true + title: Project Id + agent_id: + anyOf: + - type: string + - type: 'null' + title: Agent Id + period: + type: string + enum: + - daily + - monthly + title: Period + limit_micro: + type: integer + minimum: 0.0 + title: Limit Micro + additionalProperties: false type: object required: - - items - title: FaxJobPage - HTTPValidationError: + - period + - limit_micro + title: BudgetCreate + BudgetList: properties: - detail: + items: items: - $ref: '#/components/schemas/ValidationError' + $ref: '#/components/schemas/Budget' type: array - title: Detail + title: Items + additionalProperties: true type: object - title: HTTPValidationError - required: [] - InboxItem: + required: + - items + title: BudgetList + BudgetPatch: properties: - id: - type: string - title: Id - from: + limit_micro: anyOf: - - type: string + - type: integer + minimum: 0.0 - type: 'null' - title: From - to: - type: string - title: To - pages: - type: integer - title: Pages + title: Limit Micro status: - type: string - title: Status - document_id: - anyOf: - - type: string - - type: 'null' - title: Document Id - received_at: - type: string - title: Received At - amount_micro: - type: integer - title: Amount Micro - default: 0 - note: anyOf: - type: string + enum: + - active + - disabled - type: 'null' - title: Note + title: Status + additionalProperties: false + type: object + title: BudgetPatch + Capabilities: + properties: environment: type: string enum: - live - test title: Environment - default: live - number_id: - anyOf: - - type: string - - type: 'null' - title: Number Id - project_id: - anyOf: - - type: string - - type: 'null' - title: Project Id - partial_content: - type: boolean - title: Partial Content - default: false - media_status: - type: string - title: Media Status - default: stored - duration_secs: - anyOf: - - type: integer - - type: 'null' - title: Duration Secs provider: type: string title: Provider - default: '' - read_at: - anyOf: - - type: string - - type: 'null' - title: Read At - expires_at: - anyOf: - - type: string - - type: 'null' - title: Expires At + send: + type: boolean + title: Send + receive: + type: boolean + title: Receive + numbers: + type: boolean + title: Numbers + routes: + items: + type: string + type: array + title: Routes + formats: + items: + type: string + type: array + title: Formats + max_pages: + type: integer + title: Max Pages + max_upload_bytes: + type: integer + title: Max Upload Bytes + price_version: + type: string + title: Price Version + rates: + additionalProperties: true + type: object + title: Rates + scopes: + items: + type: string + type: array + title: Scopes + approvals: + type: boolean + title: Approvals + extraction: + type: boolean + title: Extraction + limits: + additionalProperties: + type: integer + type: object + title: Limits + one_time: + additionalProperties: true + type: object + title: One Time additionalProperties: true type: object required: - - id - - to - - pages - - status - - document_id - - received_at - title: InboxItem - InboxItemDetail: + - environment + - provider + - send + - receive + - numbers + - routes + - formats + - max_pages + - max_upload_bytes + - price_version + - rates + - scopes + - approvals + - limits + title: Capabilities + ClassificationCreate: properties: - id: - type: string - title: Id - from: + inbox_item_id: anyOf: - type: string + pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - type: 'null' - title: From - to: - type: string - title: To - pages: - type: integer - title: Pages - status: - type: string - title: Status + title: Inbox Item Id document_id: anyOf: - type: string + pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - type: 'null' title: Document Id - received_at: - type: string - title: Received At - amount_micro: - type: integer - title: Amount Micro - default: 0 - note: + max_pages: anyOf: - - type: string + - type: integer + maximum: 200.0 + minimum: 1.0 - type: 'null' - title: Note - environment: + title: Max Pages + strict: + type: boolean + title: Strict + description: Refuse with page_limit_exceeded when the document has more pages than would be read, instead + of reading the first ones and warning. + default: false + document_types: + items: + type: string + type: array + maxItems: 30 + minItems: 1 + title: Document Types + abstain_below: + type: number + maximum: 1.0 + minimum: 0.0 + title: Abstain Below + default: 0.6 + strong_model_if_below: + type: number + maximum: 1.0 + minimum: 0.0 + title: Strong Model If Below + default: 0.75 + additionalProperties: false + type: object + required: + - document_types + title: ClassificationCreate + Client: + properties: + id: + type: string + title: Id + client_id: + type: string + title: Client Id + kind: type: string enum: - - live - - test - title: Environment - default: live - number_id: - anyOf: - - type: string - - type: 'null' - title: Number Id - project_id: + - static + - dynamic + - cimd + title: Kind + client_name: anyOf: - type: string - type: 'null' - title: Project Id - partial_content: - type: boolean - title: Partial Content - default: false - media_status: + title: Client Name + client_host: type: string - title: Media Status - default: stored - duration_secs: - anyOf: - - type: integer - - type: 'null' - title: Duration Secs - provider: + title: Client Host + created_at: type: string - title: Provider - default: '' - read_at: + title: Created At + last_used_at: anyOf: - type: string - type: 'null' - title: Read At - expires_at: + title: Last Used At + disabled_at: anyOf: - type: string - type: 'null' - title: Expires At - document: + title: Disabled At + disabled_reason: anyOf: - - additionalProperties: true - type: object + - type: string - type: 'null' - title: Document - text: - anyOf: - - type: string - - type: 'null' - title: Text - extraction_id: + title: Disabled Reason + additionalProperties: true + type: object + required: + - id + - client_id + - kind + - client_name + - client_host + - created_at + - last_used_at + - disabled_at + - disabled_reason + title: Client + ClientList: + properties: + items: + items: + $ref: '#/components/schemas/Client' + type: array + title: Items + next_cursor: anyOf: - type: string - type: 'null' - title: Extraction Id + title: Next Cursor + total: + type: integer + minimum: 0.0 + title: Total additionalProperties: true type: object required: - - id - - to - - pages - - status - - document_id - - received_at - title: InboxItemDetail - InboxItemSummary: + - items + - total + title: ClientList + Correction: properties: id: type: string title: Id - from: + extraction_id: + type: string + title: Extraction Id + field: + type: string + title: Field + value: + title: Value + note: anyOf: - type: string - type: 'null' - title: From - to: + title: Note + created_by: type: string - title: To - pages: - type: integer - title: Pages - status: + title: Created By + created_at: type: string - title: Status - document_id: + title: Created At + additionalProperties: true + type: object + required: + - id + - extraction_id + - field + - value + - note + - created_by + - created_at + title: Correction + CorrectionCreate: + properties: + field: anyOf: - type: string + maxLength: 120 - type: 'null' - title: Document Id - received_at: - type: string - title: Received At - amount_micro: - type: integer - title: Amount Micro - default: 0 + title: Field + value: + title: Value note: anyOf: - type: string + maxLength: 1000 - type: 'null' title: Note - environment: - type: string - enum: - - live - - test - title: Environment - description: Returned by POST /v1/sandbox/inbound-faxes. - x-sdk-route: POST /v1/sandbox/inbound-faxes - number_id: + additionalProperties: false + type: object + title: CorrectionCreate + Coverage: + properties: + coverage_version: anyOf: - type: string - type: 'null' - title: Number Id - description: Returned by POST /v1/sandbox/inbound-faxes. - x-sdk-route: POST /v1/sandbox/inbound-faxes - project_id: + title: Coverage Version + generated_at: anyOf: - type: string - type: 'null' - title: Project Id - description: Returned by POST /v1/sandbox/inbound-faxes. - x-sdk-route: POST /v1/sandbox/inbound-faxes - partial_content: + title: Generated At + orderable_now: + items: + type: string + type: array + title: Orderable Now + outbound: + additionalProperties: true + type: object + title: Outbound + inbound: + additionalProperties: true + type: object + title: Inbound + excluded: + additionalProperties: true + type: object + title: Excluded + note: + anyOf: + - type: string + - type: 'null' + title: Note + additionalProperties: true + type: object + required: + - coverage_version + - generated_at + - orderable_now + - outbound + - inbound + - excluded + - note + title: Coverage + Deleted: + properties: + id: + type: string + title: Id + deleted: type: boolean - title: Partial Content - description: Returned by POST /v1/sandbox/inbound-faxes. - x-sdk-route: POST /v1/sandbox/inbound-faxes - media_status: + title: Deleted + default: true + additionalProperties: true + type: object + required: + - id + title: Deleted + Delivery: + properties: + id: type: string - title: Media Status - description: Returned by POST /v1/sandbox/inbound-faxes. - x-sdk-route: POST /v1/sandbox/inbound-faxes - duration_secs: + title: Id + webhook_id: + type: string + title: Webhook Id + event_id: + type: string + title: Event Id + event_type: + type: string + title: Event Type + attempt: + type: integer + title: Attempt + status: + type: string + title: Status + response_status: anyOf: - type: integer - type: 'null' - title: Duration Secs - description: Returned by POST /v1/sandbox/inbound-faxes. - x-sdk-route: POST /v1/sandbox/inbound-faxes - provider: - type: string - title: Provider - description: Returned by POST /v1/sandbox/inbound-faxes. - x-sdk-route: POST /v1/sandbox/inbound-faxes - read_at: + title: Response Status + error: anyOf: - type: string - type: 'null' - title: Read At - description: Returned by POST /v1/sandbox/inbound-faxes. - x-sdk-route: POST /v1/sandbox/inbound-faxes - expires_at: + title: Error + duration_ms: + anyOf: + - type: integer + - type: 'null' + title: Duration Ms + replay_of: anyOf: - type: string - type: 'null' - title: Expires At - description: Returned by POST /v1/sandbox/inbound-faxes. - x-sdk-route: POST /v1/sandbox/inbound-faxes - additionalProperties: true - type: object - required: - - id - - to - - pages - - status - - document_id - - received_at - title: InboxItem - InboxPage: - properties: - items: + title: Replay Of + schedule_s: items: - $ref: '#/components/schemas/InboxItem' + type: integer type: array - title: Items - next_cursor: + title: Schedule S + next_attempt_at: anyOf: - type: string - type: 'null' - title: Next Cursor + title: Next Attempt At + created_at: + type: string + title: Created At + delivered_at: + anyOf: + - type: string + - type: 'null' + title: Delivered At additionalProperties: true type: object required: - - items - title: InboxPage - InboxUpdate: - properties: - status: - anyOf: - - type: string - enum: - - received - - read - - archived - - type: 'null' - title: Status - additionalProperties: false - type: object - title: InboxUpdate - required: [] - Inventory: + - id + - webhook_id + - event_id + - event_type + - attempt + - status + - response_status + - error + - duration_ms + - replay_of + - schedule_s + - next_attempt_at + - created_at + - delivered_at + title: Delivery + DeliveryPage: properties: - country: - type: string - title: Country - number_type: - type: string - title: Number Type - locality: - anyOf: - - type: string - - type: 'null' - title: Locality items: items: - $ref: '#/components/schemas/InventoryNumber' + $ref: '#/components/schemas/Delivery' type: array title: Items - recurring_period: - type: string - title: Recurring Period - note: - type: string - title: Note - additionalProperties: true - type: object - required: - - country - - number_type - - locality - - items - - recurring_period - - note - title: Inventory - InventoryNumber: - properties: - e164: - type: string - title: E164 - country: - type: string - title: Country - number_type: - type: string - title: Number Type - locality: + next_cursor: anyOf: - type: string - type: 'null' - title: Locality - monthly_micro: - type: integer - title: Monthly Micro - currency: - type: string - title: Currency - setup_micro: - type: integer - title: Setup Micro + title: Next Cursor additionalProperties: true type: object required: - - e164 - - country - - number_type - - locality - - monthly_micro - - currency - - setup_micro - title: InventoryNumber - Invitation: + - items + title: DeliveryPage + Document: properties: id: type: string title: Id - email: - type: string - title: Email - role: + environment: type: string enum: - - owner - - admin - - member - - viewer - title: Role - invited_by: + - live + - test + title: Environment + status: type: string - title: Invited By + title: Status + source: + type: string + title: Source + filename: + type: string + title: Filename + content_type: + type: string + title: Content Type + size_bytes: + type: integer + title: Size Bytes + sha256: + type: string + title: Sha256 + page_count: + type: integer + title: Page Count + render_status: + type: string + title: Render Status + render_reason: + anyOf: + - type: string + - type: 'null' + title: Render Reason + validation: + additionalProperties: true + type: object + title: Validation + upload_id: + anyOf: + - type: string + - type: 'null' + title: Upload Id created_at: type: string title: Created At expires_at: - type: string + anyOf: + - type: string + - type: 'null' title: Expires At - accepted_at: + deleted_at: anyOf: - type: string - type: 'null' - title: Accepted At + title: Deleted At additionalProperties: true type: object required: - id - - email - - role - - invited_by + - environment + - status + - source + - filename + - content_type + - size_bytes + - sha256 + - page_count + - render_status + - render_reason + - validation + - upload_id - created_at - expires_at - - accepted_at - title: Invitation - InvitationCreate: - properties: - email: - type: string - format: email - title: Email - role: - type: string - enum: - - owner - - admin - - member - - viewer - title: Role - default: member - additionalProperties: false - type: object - required: - - email - title: InvitationCreate - InvitationList: - properties: - items: - items: - $ref: '#/components/schemas/Invitation' - type: array - title: Items - additionalProperties: true - type: object - required: - - items - title: InvitationList - Invoice: + - deleted_at + title: Document + DocumentCreate: properties: - id: - type: string - title: Id - number: - anyOf: - - type: string - - type: 'null' - title: Number - status: + upload_id: anyOf: - type: string - type: 'null' - title: Status - currency: - type: string - title: Currency - amount_due_micro: - type: integer - title: Amount Due Micro - amount_paid_micro: - type: integer - title: Amount Paid Micro - subscription_id: + title: Upload Id + text: anyOf: - type: string - type: 'null' - title: Subscription Id - hosted_invoice_url: + title: Text + description: Plain text rendered to a letter PDF. + examples: + - Hello from Fax for Agents + markdown: anyOf: - type: string - type: 'null' - title: Hosted Invoice Url - invoice_pdf: + title: Markdown + description: Markdown (headings, lists, emphasis, code) rendered to PDF. + html: anyOf: - type: string - type: 'null' - title: Invoice Pdf - created_at: + title: Html + description: 'HTML rendered to PDF by a headless browser with scripts off and no network: inline CSS and + data: images print, external resources are counted as blocked. US letter unless @page says otherwise. + At most 2 MiB.' + filename: anyOf: - type: string + maxLength: 255 - type: 'null' - title: Created At - additionalProperties: true + title: Filename + additionalProperties: false type: object - required: - - id - - number - - status - - currency - - amount_due_micro - - amount_paid_micro - - subscription_id - - hosted_invoice_url - - invoice_pdf - - created_at - title: Invoice - InvoicePage: + title: DocumentCreate + description: Send exactly one of text, markdown, html or upload_id. + DocumentPage: properties: items: items: - $ref: '#/components/schemas/Invoice' + $ref: '#/components/schemas/Document' type: array title: Items next_cursor: @@ -10962,432 +11739,504 @@ components: type: object required: - items - title: InvoicePage - LedgerEntry: + title: DocumentPage + DocumentPages: properties: - id: - type: string - title: Id - txn_id: - type: string - title: Txn Id - account: + document_id: type: string - title: Account - amount_micro: + title: Document Id + page_count: type: integer - title: Amount Micro - kind: + title: Page Count + render_status: type: string - title: Kind - ref_type: - anyOf: - - type: string - - type: 'null' - title: Ref Type - ref_id: - anyOf: - - type: string - - type: 'null' - title: Ref Id - memo: + title: Render Status + render_reason: anyOf: - type: string - type: 'null' - title: Memo - created_at: - type: string - title: Created At - additionalProperties: true - type: object - required: - - id - - txn_id - - account - - amount_micro - - kind - - ref_type - - ref_id - - memo - - created_at - title: LedgerEntry - LedgerPage: - properties: + title: Render Reason items: items: - $ref: '#/components/schemas/LedgerEntry' + $ref: '#/components/schemas/PagePreview' type: array title: Items - next_cursor: - anyOf: - - type: string - - type: 'null' - title: Next Cursor additionalProperties: true type: object required: + - document_id + - page_count + - render_status + - render_reason - items - title: LedgerPage - Me: + title: DocumentPages + DocumentValidation: properties: - org_id: + id: type: string - title: Org Id - project_id: + title: Id + status: type: string - title: Project Id - key_id: + title: Status + page_count: + type: integer + title: Page Count + render_status: type: string - title: Key Id - key_prefix: + title: Render Status + render_reason: + anyOf: + - type: string + - type: 'null' + title: Render Reason + warnings: + items: + additionalProperties: true + type: object + type: array + title: Warnings + additionalProperties: true + type: object + required: + - id + - status + - page_count + - render_status + - render_reason + - warnings + title: DocumentValidation + EffectivePolicies: + properties: + actor: type: string - title: Key Prefix + title: Actor environment: type: string enum: - live - test title: Environment - scopes: + under_policy: + type: boolean + title: Under Policy + approval_required_without_policy: + type: boolean + title: Approval Required Without Policy + requires_human_approval: + type: boolean + title: Requires Human Approval + description: 'Server-set for hosted integration clients and their keys: every send needs a person''s approval, + whatever the policies say.' + default: false + policies: + items: + $ref: '#/components/schemas/Policy' + type: array + title: Policies + rules_order: items: type: string type: array - title: Scopes - actor: - type: string - title: Actor + title: Rules Order additionalProperties: true type: object required: - - org_id - - project_id - - key_id - - key_prefix - - environment - - scopes - actor - title: Me - Member: + - environment + - under_policy + - approval_required_without_policy + - policies + - rules_order + title: EffectivePolicies + ErrorEnvelope: + title: ErrorEnvelope + type: object + required: + - error + properties: + error: + type: object + required: + - code + - message + - retryable + - correlation_id + properties: + code: + type: string + description: A stable code such as invalid_token, scope_missing, not_found, quote_expired, insufficient_balance, + approval_required, rate_limited, live_provider_unavailable, one_time_unavailable. + message: + type: string + description: One sentence a person can act on. + retryable: + type: boolean + correlation_id: + type: string + description: The X-Request-Id of the request. Quote it when you write to hello@faxforagents.com. + details: + type: object + additionalProperties: true + description: Code-specific fields, for example details.scope on scope_missing. + Event: properties: id: type: string title: Id - email: + type: type: string - title: Email - name: + title: Type + created_at: + type: string + title: Created At + org_id: + type: string + title: Org Id + environment: + type: string + enum: + - live + - test + title: Environment + project_id: anyOf: - type: string - type: 'null' - title: Name - role: + title: Project Id + resource_type: type: string - enum: - - owner - - admin - - member - - viewer - title: Role - user_id: + title: Resource Type + resource_id: + type: string + title: Resource Id + fax_job_id: anyOf: - type: string - type: 'null' - title: User Id - created_at: - type: string - title: Created At + title: Fax Job Id + schema_version: + type: integer + title: Schema Version + correlation_id: + anyOf: + - type: string + - type: 'null' + title: Correlation Id + data: + additionalProperties: true + type: object + title: Data additionalProperties: true type: object required: - id - - email - - name - - role - - user_id + - type - created_at - title: Member - MemberList: - properties: + - org_id + - environment + - project_id + - resource_type + - resource_id + - fax_job_id + - schema_version + - correlation_id + - data + title: Event + EventPage: + properties: items: items: - $ref: '#/components/schemas/Member' + $ref: '#/components/schemas/Event' type: array title: Items - additionalProperties: true - type: object - required: - - items - title: MemberList - MemberPatch: - properties: - role: + next_cursor: anyOf: - type: string - enum: - - owner - - admin - - member - - viewer - type: 'null' - title: Role - description: Required. Optional in the schema so a foreign id is a 404 first. - additionalProperties: false + title: Next Cursor + additionalProperties: true type: object - title: MemberPatch - required: [] - Number: + required: + - items + title: EventPage + Extraction: properties: id: type: string title: Id + kind: + type: string + enum: + - extract + - classify + - summarize + title: Kind + status: + type: string + enum: + - pending + - queued + - running + - completed + - failed + title: Status environment: type: string enum: - live - test title: Environment - e164: - type: string - title: E164 - country: - type: string - title: Country - number_type: + document_id: type: string - title: Number Type - locality: + title: Document Id + inbox_item_id: anyOf: - type: string - type: 'null' - title: Locality - status: - type: string - title: Status - label: + title: Inbox Item Id + schema_id: anyOf: - type: string - type: 'null' - title: Label - project_id: + title: Schema Id + schema: anyOf: - - type: string + - additionalProperties: true + type: object - type: 'null' - title: Project Id - inbound_webhook_id: + title: Schema + options: + additionalProperties: true + type: object + title: Options + pages_requested: + type: integer + title: Pages Requested + pages_processed: + type: integer + title: Pages Processed + pages_escalated: + type: integer + title: Pages Escalated + document_pages: + type: integer + title: Document Pages + description: The document's page count. Above pages_processed on a completed row means the cap left pages + unread; output.truncated and a warning say so. + confidence: anyOf: - - type: string + - type: number - type: 'null' - title: Inbound Webhook Id - inbound_status: - type: string - title: Inbound Status - provider: - type: string - title: Provider - monthly_micro: + title: Confidence + pages: + items: + $ref: '#/components/schemas/ExtractionPage' + type: array + title: Pages + output: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Output + models: + additionalProperties: true + type: object + title: Models + price_micro: type: integer - title: Monthly Micro + title: Price Micro + provider_cost_micro: + type: integer + title: Provider Cost Micro currency: type: string title: Currency - order_id: + error_code: anyOf: - type: string - type: 'null' - title: Order Id + title: Error Code + created_by: + type: string + title: Created By created_at: type: string title: Created At - activated_at: + started_at: anyOf: - type: string - type: 'null' - title: Activated At - renews_at: + title: Started At + completed_at: anyOf: - type: string - type: 'null' - title: Renews At - released_at: + title: Completed At + corrections: anyOf: - - type: string + - items: + additionalProperties: true + type: object + type: array - type: 'null' - title: Released At + title: Corrections additionalProperties: true type: object required: - id - - environment - - e164 - - country - - number_type - - locality + - kind - status - - label - - project_id - - inbound_webhook_id - - inbound_status - - provider - - monthly_micro + - environment + - document_id + - inbox_item_id + - schema_id + - options + - pages_requested + - pages_processed + - pages_escalated + - document_pages + - confidence + - pages + - output + - models + - price_micro + - provider_cost_micro - currency - - order_id + - error_code + - created_by - created_at - - activated_at - - renews_at - - released_at - title: Number - NumberList: - properties: - items: - items: - $ref: '#/components/schemas/Number' - type: array - title: Items - additionalProperties: true - type: object - required: - - items - title: NumberList - NumberOrder: + - started_at + - completed_at + title: Extraction + ExtractionCreate: properties: - id: - type: string - title: Id - environment: - type: string - enum: - - live - - test - title: Environment - status: - type: string - title: Status - e164: + inbox_item_id: anyOf: - type: string + pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - type: 'null' - title: E164 - country: + title: Inbox Item Id + document_id: anyOf: - type: string + pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - type: 'null' - title: Country - number_type: + title: Document Id + max_pages: anyOf: - - type: string + - type: integer + maximum: 200.0 + minimum: 1.0 - type: 'null' - title: Number Type - number_id: + title: Max Pages + strict: + type: boolean + title: Strict + description: Refuse with page_limit_exceeded when the document has more pages than would be read, instead + of reading the first ones and warning. + default: false + schema: anyOf: - - type: string + - additionalProperties: true + type: object - type: 'null' - title: Number Id - number: + title: Schema + description: A JSON Schema object of string, number, boolean or date fields with descriptions. + schema_id: anyOf: - - $ref: '#/components/schemas/Number' + - type: string + pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - type: 'null' - provider: - type: string - title: Provider - recurring_charge_micro: - type: integer - title: Recurring Charge Micro - description: Charged every month on the activation day. - recurring_period: - type: string - title: Recurring Period - first_month_hold_micro: + title: Schema Id + strong_model_if_below: + type: number + maximum: 1.0 + minimum: 0.0 + title: Strong Model If Below + default: 0.75 + additionalProperties: false + type: object + title: ExtractionCreate + ExtractionPage: + properties: + page: type: integer - title: First Month Hold Micro - description: Held now, captured when the number activates. - currency: + title: Page + text: type: string - title: Currency - error: + title: Text + confidence: + type: number + title: Confidence + language: anyOf: - type: string - type: 'null' - title: Error - created_at: + title: Language + status: type: string - title: Created At - completed_at: - anyOf: - - type: string - - type: 'null' - title: Completed At + title: Status + model: + type: string + title: Model + escalated: + type: boolean + title: Escalated + default: false + warnings: + items: + type: string + type: array + title: Warnings + default: [] + fast_read: + type: boolean + description: Returned by GET /v1/extractions/{extraction_id}. Declared here until the public schema includes + it. + x-sdk-route: GET /v1/extractions/{extraction_id} + strong_read: + type: boolean + description: Returned by GET /v1/extractions/{extraction_id}. Declared here until the public schema includes + it. + x-sdk-route: GET /v1/extractions/{extraction_id} + cost_micro: + type: integer + description: Returned by GET /v1/extractions/{extraction_id}. Declared here until the public schema includes + it. + x-sdk-route: GET /v1/extractions/{extraction_id} + words_count: + type: integer + description: Returned by GET /v1/extractions/{extraction_id}. Declared here until the public schema includes + it. + x-sdk-route: GET /v1/extractions/{extraction_id} additionalProperties: true type: object required: - - id - - environment + - page + - text + - confidence - status - - e164 - - country - - number_type - - number_id - - number - - provider - - recurring_charge_micro - - recurring_period - - first_month_hold_micro - - currency - - error - - created_at - - completed_at - title: NumberOrder - NumberOrderCreate: + - model + title: ExtractionPage + ExtractionPageList: properties: - e164: - type: string - title: E164 - description: A number from GET /v1/number-inventory, E.164. - label: - anyOf: - - type: string - maxLength: 120 - - type: 'null' - title: Label - project_id: - anyOf: - - type: string - - type: 'null' - title: Project Id - description: Inbound faxes are filed under this project. - inbound_webhook_id: + items: + items: + $ref: '#/components/schemas/Extraction' + type: array + title: Items + next_cursor: anyOf: - type: string - type: 'null' - title: Inbound Webhook Id - description: 'The webhook that receives fax.received for this number (default: all).' - additionalProperties: false + title: Next Cursor + additionalProperties: true type: object required: - - e164 - title: NumberOrderCreate - NumberUpdate: - properties: - label: - anyOf: - - type: string - maxLength: 120 - - type: 'null' - title: Label - project_id: - anyOf: - - type: string - - type: 'null' - title: Project Id - inbound_webhook_id: - anyOf: - - type: string - - type: 'null' - title: Inbound Webhook Id - additionalProperties: false - type: object - title: NumberUpdate - required: [] - Organization: + - items + title: ExtractionPageList + ExtractionSchema: properties: id: type: string @@ -11395,996 +12244,1139 @@ components: name: type: string title: Name - role: + version: + type: integer + title: Version + description: anyOf: - type: string - enum: - - owner - - admin - - member - - viewer - type: 'null' - title: Role + title: Description + schema: + additionalProperties: true + type: object + title: Schema + status: + type: string + title: Status + created_by: + type: string + title: Created By created_at: type: string title: Created At - members: - type: integer - title: Members additionalProperties: true type: object required: - id - name - - role + - version + - description + - schema + - status + - created_by - created_at - - members - title: Organization - OrganizationList: + title: ExtractionSchema + ExtractionSchemaList: properties: items: items: - $ref: '#/components/schemas/OrganizationSummary' + $ref: '#/components/schemas/ExtractionSchema' type: array title: Items additionalProperties: true type: object required: - items - title: OrganizationList - OrganizationPatch: + title: ExtractionSchemaList + FaxCreate: properties: - name: + quote_id: type: string - maxLength: 200 - minLength: 1 - title: Name + title: Quote Id + client_reference: + anyOf: + - type: string + maxLength: 200 + - type: 'null' + title: Client Reference + approval_id: + anyOf: + - type: string + pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ + - type: 'null' + title: Approval Id + description: An approved approval request bound to this quote. Consumed by this one send. additionalProperties: false type: object required: - - name - title: OrganizationPatch - OrganizationSummary: + - quote_id + title: FaxCreate + description: |- + M9: an approved request authorizes the send; without one a quote that needs authorization is + evaluated against the caller's delegated policies. + FaxJob: properties: id: type: string title: Id - name: + environment: type: string - title: Name - role: + enum: + - live + - test + title: Environment + direction: + type: string + title: Direction + state: type: string enum: - - owner - - admin - - member - - viewer - title: Role - additionalProperties: true - type: object - required: - - id - - name - - role - title: OrganizationSummary - PagePreview: - properties: - page_number: - type: integer - title: Page Number - width_px: + - queued + - active + - partial + - failed + - unknown + - delivered + title: State + failure_code: anyOf: - - type: integer + - type: string - type: 'null' - title: Width Px - height_px: + title: Failure Code + to: + type: string + title: To + from: anyOf: - - type: integer + - type: string - type: 'null' - title: Height Px - url: + title: From + route: type: string - title: Url - additionalProperties: true - type: object - required: - - page_number - - width_px - - height_px - - url - title: PagePreview - Policy: - properties: - id: + title: Route + document_id: type: string - title: Id - name: + title: Document Id + document_sha256: type: string - title: Name - purpose: + title: Document Sha256 + transmitted_sha256: anyOf: - type: string - type: 'null' - title: Purpose - enabled: - type: boolean - title: Enabled - version: + title: Transmitted Sha256 + quote_id: + anyOf: + - type: string + - type: 'null' + title: Quote Id + billable_pages: type: integer - title: Version - current_version_id: + title: Billable Pages + cover_pages: + type: integer + title: Cover Pages + quality: + type: string + title: Quality + default: fine + sender_name: anyOf: - type: string - type: 'null' - title: Current Version Id - rules: - additionalProperties: true - type: object - title: Rules - created_by: + title: Sender Name + delete_after_send: + type: boolean + title: Delete After Send + default: false + pages_sent: + type: integer + title: Pages Sent + amount_micro: + type: integer + title: Amount Micro + captured_micro: + type: integer + title: Captured Micro + currency: type: string - title: Created By + title: Currency + provider: + type: string + title: Provider + provider_ref: + anyOf: + - type: string + - type: 'null' + title: Provider Ref + client_reference: + anyOf: + - type: string + - type: 'null' + title: Client Reference + attempt_count: + type: integer + title: Attempt Count created_at: type: string title: Created At updated_at: type: string title: Updated At - managed: + completed_at: anyOf: - type: string - type: 'null' - title: Managed - description: connected_apps when a Settings switch (the auto-send setting) manages this policy. Such a - policy is read here and changed only through PUT /v1/organizations/current/auto-send. + title: Completed At + cancel_requested_at: + anyOf: + - type: string + - type: 'null' + title: Cancel Requested At + evidence: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Evidence + description: The carrier's evidence once delivered or partial; null before. + one_time_order_id: + anyOf: + - type: string + - type: 'null' + title: One Time Order Id + description: The one-time order this fax was paid by; null for a wallet fax. + one_time: + anyOf: + - $ref: '#/components/schemas/OneTimeJob' + - type: 'null' + approval_id: + type: + - string + - 'null' + description: Returned by GET /v1/faxes/{fax_id}. Declared here until the public schema includes it. + x-sdk-route: GET /v1/faxes/{fax_id} + policy_version_id: + type: + - string + - 'null' + description: Returned by GET /v1/faxes/{fax_id}. Declared here until the public schema includes it. + x-sdk-route: GET /v1/faxes/{fax_id} additionalProperties: true type: object required: - id - - name - - purpose - - enabled - - version - - current_version_id - - rules - - created_by + - environment + - direction + - state + - failure_code + - to + - route + - document_id + - document_sha256 + - transmitted_sha256 + - quote_id + - billable_pages + - cover_pages + - pages_sent + - amount_micro + - captured_micro + - currency + - provider + - provider_ref + - client_reference + - attempt_count - created_at - updated_at - title: Policy - PolicyCreate: + - completed_at + - cancel_requested_at + title: FaxJob + FaxJobPage: properties: - name: - type: string - maxLength: 120 - minLength: 1 - title: Name - purpose: + items: + items: + $ref: '#/components/schemas/FaxJob' + type: array + title: Items + next_cursor: anyOf: - type: string - maxLength: 500 - type: 'null' - title: Purpose - description: Why this policy exists. Shown, never evaluated. - rules: - additionalProperties: true - type: object - title: Rules - description: See the PolicyRules schema in docs/APPROVALS.md. - enabled: - type: boolean - title: Enabled - default: true - additionalProperties: false + title: Next Cursor + additionalProperties: true type: object required: - - name - - rules - title: PolicyCreate - PolicyEvaluation: + - items + title: FaxJobPage + Grant: properties: - decision: + id: type: string - title: Decision - rule: + title: Id + org_id: + type: string + title: Org Id + organization: + type: string + title: Organization + user_id: + type: string + title: User Id + client_id: + type: string + title: Client Id + client_host: + type: string + title: Client Host + client_name: anyOf: - type: string - type: 'null' - title: Rule - reason: + title: Client Name + environment: type: string - title: Reason - policy_id: + enum: + - test + - live + title: Environment + scopes: + items: + type: string + type: array + title: Scopes + created_at: + type: string + title: Created At + last_used_at: anyOf: - type: string - type: 'null' - title: Policy Id - policy_version_id: + title: Last Used At + revoked_at: anyOf: - type: string - type: 'null' - title: Policy Version Id - policy_name: + title: Revoked At + additionalProperties: true + type: object + required: + - id + - org_id + - organization + - user_id + - client_id + - client_host + - client_name + - environment + - scopes + - created_at + - last_used_at + - revoked_at + title: Grant + GrantList: + properties: + items: + items: + $ref: '#/components/schemas/Grant' + type: array + title: Items + next_cursor: anyOf: - type: string - type: 'null' - title: Policy Name - version: - anyOf: - - type: integer - - type: 'null' - title: Version - denials: - items: - additionalProperties: true - type: object - type: array - title: Denials - plan: - additionalProperties: true - type: object - title: Plan - executed: - type: boolean - title: Executed - default: false - usage: - type: - - object - - 'null' - additionalProperties: - type: integer - description: Returned by POST /v1/policy-evaluations when a bound policy has usage. - x-sdk-route: POST /v1/policy-evaluations + title: Next Cursor additionalProperties: true type: object required: - - decision - - rule - - reason - - policy_id - - policy_version_id - - policy_name - - version - - denials - - plan - title: PolicyEvaluation - PolicyEvaluationCreate: + - items + title: GrantList + HTTPValidationError: properties: - quote_id: + detail: + items: + $ref: '#/components/schemas/ValidationError' + type: array + title: Detail + type: object + title: HTTPValidationError + InboxItem: + properties: + id: + type: string + title: Id + from: anyOf: - type: string - pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - type: 'null' - title: Quote Id - description: Evaluate an existing quote. + title: From + to: + type: string + title: To + pages: + type: integer + title: Pages + status: + type: string + title: Status document_id: anyOf: - type: string - pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - type: 'null' title: Document Id - description: 'Or plan: this document ...' - to: + received_at: + type: string + title: Received At + amount_micro: + type: integer + title: Amount Micro + default: 0 + note: anyOf: - type: string - maxLength: 32 - type: 'null' - title: To - description: '... to this E.164 destination.' - from: + title: Note + environment: + type: string + enum: + - live + - test + title: Environment + default: live + number_id: anyOf: - type: string - maxLength: 32 - type: 'null' - title: From - additionalProperties: false - type: object - title: PolicyEvaluationCreate - required: [] - PolicyList: - properties: - items: - items: - $ref: '#/components/schemas/Policy' - type: array - title: Items - additionalProperties: true - type: object - required: - - items - title: PolicyList - PolicyUpdate: - properties: - name: + title: Number Id + project_id: anyOf: - type: string - maxLength: 120 - minLength: 1 - type: 'null' - title: Name - purpose: + title: Project Id + partial_content: + type: boolean + title: Partial Content + default: false + media_status: + type: string + title: Media Status + default: stored + duration_secs: anyOf: - - type: string - maxLength: 500 + - type: integer - type: 'null' - title: Purpose - rules: + title: Duration Secs + provider: + type: string + title: Provider + default: '' + read_at: anyOf: - - additionalProperties: true - type: object + - type: string - type: 'null' - title: Rules - enabled: + title: Read At + expires_at: anyOf: - - type: boolean + - type: string - type: 'null' - title: Enabled - additionalProperties: false + title: Expires At + additionalProperties: true type: object - title: PolicyUpdate - required: [] - PolicyVersion: + required: + - id + - to + - pages + - status + - document_id + - received_at + title: InboxItem + InboxItemDetail: properties: id: type: string title: Id - policy_id: + from: + anyOf: + - type: string + - type: 'null' + title: From + to: type: string - title: Policy Id - version: + title: To + pages: type: integer - title: Version - name: + title: Pages + status: type: string - title: Name - purpose: + title: Status + document_id: anyOf: - type: string - type: 'null' - title: Purpose - enabled: - type: boolean - title: Enabled - rules: - additionalProperties: true - type: object - title: Rules - created_by: + title: Document Id + received_at: type: string - title: Created By - created_at: + title: Received At + amount_micro: + type: integer + title: Amount Micro + default: 0 + note: + anyOf: + - type: string + - type: 'null' + title: Note + environment: type: string - title: Created At - additionalProperties: true - type: object - required: - - id - - policy_id - - version - - name - - purpose - - enabled - - rules - - created_by - - created_at - title: PolicyVersion - PolicyVersionList: - properties: - items: - items: - $ref: '#/components/schemas/PolicyVersion' - type: array - title: Items + enum: + - live + - test + title: Environment + default: live + number_id: + anyOf: + - type: string + - type: 'null' + title: Number Id + project_id: + anyOf: + - type: string + - type: 'null' + title: Project Id + partial_content: + type: boolean + title: Partial Content + default: false + media_status: + type: string + title: Media Status + default: stored + duration_secs: + anyOf: + - type: integer + - type: 'null' + title: Duration Secs + provider: + type: string + title: Provider + default: '' + read_at: + anyOf: + - type: string + - type: 'null' + title: Read At + expires_at: + anyOf: + - type: string + - type: 'null' + title: Expires At + document: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Document + text: + anyOf: + - type: string + - type: 'null' + title: Text + extraction_id: + anyOf: + - type: string + - type: 'null' + title: Extraction Id additionalProperties: true type: object required: - - items - title: PolicyVersionList - PortOrder: + - id + - to + - pages + - status + - document_id + - received_at + title: InboxItemDetail + InboxItemSummary: properties: id: type: string title: Id + from: + anyOf: + - type: string + - type: 'null' + title: From + to: + type: string + title: To + pages: + type: integer + title: Pages + status: + type: string + title: Status + document_id: + anyOf: + - type: string + - type: 'null' + title: Document Id + received_at: + type: string + title: Received At + amount_micro: + type: integer + title: Amount Micro + default: 0 + note: + anyOf: + - type: string + - type: 'null' + title: Note environment: type: string enum: - live - test title: Environment - direction: - type: string - title: Direction - status: + description: Returned by POST /v1/sandbox/inbound-faxes. + x-sdk-route: POST /v1/sandbox/inbound-faxes + number_id: + anyOf: + - type: string + - type: 'null' + title: Number Id + description: Returned by POST /v1/sandbox/inbound-faxes. + x-sdk-route: POST /v1/sandbox/inbound-faxes + project_id: + anyOf: + - type: string + - type: 'null' + title: Project Id + description: Returned by POST /v1/sandbox/inbound-faxes. + x-sdk-route: POST /v1/sandbox/inbound-faxes + partial_content: + type: boolean + title: Partial Content + description: Returned by POST /v1/sandbox/inbound-faxes. + x-sdk-route: POST /v1/sandbox/inbound-faxes + media_status: type: string - title: Status - numbers: - items: - type: string - type: array - title: Numbers - request: - additionalProperties: true - type: object - title: Request - milestones: - items: - additionalProperties: true - type: object - type: array - title: Milestones + title: Media Status + description: Returned by POST /v1/sandbox/inbound-faxes. + x-sdk-route: POST /v1/sandbox/inbound-faxes + duration_secs: + anyOf: + - type: integer + - type: 'null' + title: Duration Secs + description: Returned by POST /v1/sandbox/inbound-faxes. + x-sdk-route: POST /v1/sandbox/inbound-faxes provider: type: string title: Provider - created_at: - type: string - title: Created At - updated_at: - type: string - title: Updated At + description: Returned by POST /v1/sandbox/inbound-faxes. + x-sdk-route: POST /v1/sandbox/inbound-faxes + read_at: + anyOf: + - type: string + - type: 'null' + title: Read At + description: Returned by POST /v1/sandbox/inbound-faxes. + x-sdk-route: POST /v1/sandbox/inbound-faxes + expires_at: + anyOf: + - type: string + - type: 'null' + title: Expires At + description: Returned by POST /v1/sandbox/inbound-faxes. + x-sdk-route: POST /v1/sandbox/inbound-faxes additionalProperties: true type: object required: - id - - environment - - direction + - to + - pages - status - - numbers - - request - - milestones - - provider - - created_at - - updated_at - title: PortOrder - PortOrderCreate: + - document_id + - received_at + title: InboxItem + InboxPage: properties: - numbers: + items: items: - type: string + $ref: '#/components/schemas/InboxItem' type: array - maxItems: 50 - minItems: 1 - title: Numbers - note: + title: Items + next_cursor: anyOf: - type: string - maxLength: 500 - type: 'null' - title: Note - additionalProperties: false - type: object - required: - - numbers - title: PortOrderCreate - PortOrderList: - properties: - items: - items: - $ref: '#/components/schemas/PortOrder' - type: array - title: Items + title: Next Cursor additionalProperties: true type: object required: - items - title: PortOrderList - PortOutRequestCreate: + title: InboxPage + InboxUpdate: properties: - number_id: - type: string - title: Number Id - note: + status: anyOf: - type: string - maxLength: 500 + enum: + - received + - read + - archived - type: 'null' - title: Note + title: Status additionalProperties: false type: object - required: - - number_id - title: PortOutRequestCreate - PortabilityCheck: - properties: - items: - items: - additionalProperties: true - type: object - type: array - title: Items - checked_at: - type: string - title: Checked At - additionalProperties: true + title: InboxUpdate + InsufficientBalance: + title: InsufficientBalance + description: The error envelope of 402 insufficient_balance, with its details written out. type: object required: - - items - - checked_at - title: PortabilityCheck - PortabilityCheckCreate: + - error properties: - numbers: + error: + type: object + required: + - code + - message + - retryable + - correlation_id + - details + properties: + code: + type: string + const: insufficient_balance + message: + type: string + retryable: + type: boolean + correlation_id: + type: string + details: + type: object + additionalProperties: true + required: + - available_micro + - needed_micro + properties: + available_micro: + type: integer + description: What the wallet holds that is not on hold. + needed_micro: + type: integer + description: What this fax holds. + Inventory: + properties: + country: + type: string + title: Country + number_type: + type: string + title: Number Type + locality: + anyOf: + - type: string + - type: 'null' + title: Locality + items: items: - type: string + $ref: '#/components/schemas/InventoryNumber' type: array - maxItems: 50 - minItems: 1 - title: Numbers - additionalProperties: false + title: Items + recurring_period: + type: string + title: Recurring Period + note: + type: string + title: Note + additionalProperties: true type: object required: - - numbers - title: PortabilityCheckCreate - PortalSession: + - country + - number_type + - locality + - items + - recurring_period + - note + title: Inventory + InventoryNumber: properties: - url: + e164: type: string - title: Url - expires_at: + title: E164 + country: type: string - title: Expires At + title: Country + number_type: + type: string + title: Number Type + locality: + anyOf: + - type: string + - type: 'null' + title: Locality + monthly_micro: + type: integer + title: Monthly Micro + currency: + type: string + title: Currency + setup_micro: + type: integer + title: Setup Micro additionalProperties: true type: object required: - - url - - expires_at - title: PortalSession - Quote: + - e164 + - country + - number_type + - locality + - monthly_micro + - currency + - setup_micro + title: InventoryNumber + Invitation: properties: id: type: string title: Id - environment: + email: + type: string + title: Email + role: type: string enum: - - live - - test - title: Environment - document_id: + - owner + - admin + - member + - viewer + title: Role + invited_by: type: string - title: Document Id - document_sha256: + title: Invited By + created_at: type: string - title: Document Sha256 - to: + title: Created At + expires_at: type: string - title: To - from: + title: Expires At + accepted_at: anyOf: - type: string - type: 'null' - title: From - route: + title: Accepted At + additionalProperties: true + type: object + required: + - id + - email + - role + - invited_by + - created_at + - expires_at + - accepted_at + title: Invitation + InvitationCreate: + properties: + email: type: string - title: Route - document_pages: - type: integer - title: Document Pages - cover_pages: - type: integer - title: Cover Pages - billable_pages: - type: integer - title: Billable Pages - amount_micro: - type: integer - title: Amount Micro + format: email + title: Email + role: + type: string + enum: + - owner + - admin + - member + - viewer + title: Role + default: member + additionalProperties: false + type: object + required: + - email + title: InvitationCreate + InvitationList: + properties: + items: + items: + $ref: '#/components/schemas/Invitation' + type: array + title: Items + additionalProperties: true + type: object + required: + - items + title: InvitationList + Invoice: + properties: + id: + type: string + title: Id + number: + anyOf: + - type: string + - type: 'null' + title: Number + status: + anyOf: + - type: string + - type: 'null' + title: Status currency: type: string title: Currency - price_version: - type: string - title: Price Version - per_page_micro: + amount_due_micro: type: integer - title: Per Page Micro - default: 0 - route_label: - type: string - title: Route Label - default: '' - cover: + title: Amount Due Micro + amount_paid_micro: + type: integer + title: Amount Paid Micro + subscription_id: anyOf: - - additionalProperties: true - type: object + - type: string - type: 'null' - title: Cover - quality: - type: string - title: Quality - default: fine - sender_name: + title: Subscription Id + hosted_invoice_url: anyOf: - type: string - type: 'null' - title: Sender Name - delete_after_send: - type: boolean - title: Delete After Send - default: false - authorization_required: - type: boolean - title: Authorization Required - warnings: - items: - additionalProperties: true - type: object - type: array - title: Warnings - expires_at: - type: string - title: Expires At - expired: - type: boolean - title: Expired - consumed_by_job_id: + title: Hosted Invoice Url + invoice_pdf: anyOf: - type: string - type: 'null' - title: Consumed By Job Id + title: Invoice Pdf created_at: - type: string + anyOf: + - type: string + - type: 'null' title: Created At - pricing: - type: string - description: Returned by POST /v1/fax-quotes. Declared here until the public schema includes it. - x-sdk-route: POST /v1/fax-quotes - policy_evaluation: - type: - - object - - 'null' - description: Returned by GET /v1/fax-quotes/{quote_id}. Declared here until the public schema includes - it. - x-sdk-route: GET /v1/fax-quotes/{quote_id} - additionalProperties: true additionalProperties: true type: object required: - id - - environment - - document_id - - document_sha256 - - to - - route - - document_pages - - cover_pages - - billable_pages - - amount_micro + - number + - status - currency - - price_version - - cover - - authorization_required - - warnings - - expires_at - - expired - - consumed_by_job_id + - amount_due_micro + - amount_paid_micro + - subscription_id + - hosted_invoice_url + - invoice_pdf - created_at - title: Quote - QuoteCreate: + title: Invoice + InvoicePage: properties: - document_id: - type: string - title: Document Id - quality: - type: string - enum: - - standard - - fine - - superfine - - ultra - title: Quality - description: 'Fax resolution. standard is the fax standard''s 204 by 98 dpi, fine (the default) 204 by - 196, superfine 204 by 392. ultra asks the carrier for its darkest text processing at the same superfine - resolution and takes longer to process: measured 2026-11-09, a page sent at ultra and one at superfine - both arrived as 1728 by 4112 pixels, 204 by 391 dpi. The price does not change. The job echoes the level.' - default: fine - sender_name: - anyOf: - - type: string - maxLength: 128 - - type: 'null' - title: Sender Name - description: The station name the carrier prints in the fax header, next to the sending number. Letters, - digits, spaces and - _ ~ ! . + only, at most 128 characters. Omit to show the number alone. - delete_after_send: - type: boolean - title: Delete After Send - description: 'Delete the document and the transmitted copy the moment the fax ends in delivered, partial - or failed. The job, its receipt and the carrier evidence stay; they hold no document content. A fax - that ends unknown keeps the document until a reconcile settles it. Default: keep the document for the - retention period.' - default: false - to: - type: string - title: To - description: Destination in E.164, for example +12025550123. - from: + items: + items: + $ref: '#/components/schemas/Invoice' + type: array + title: Items + next_cursor: anyOf: - type: string - type: 'null' - title: From - description: A number you own; omit for the shared pool. - cover: - anyOf: - - additionalProperties: true - type: object - - type: 'null' - title: Cover - description: 'Cover sheet fields: to_name, from_name, subject, message. Counts as a page.' - additionalProperties: false + title: Next Cursor + additionalProperties: true type: object required: - - document_id - - to - title: QuoteCreate - Receipt: + - items + title: InvoicePage + LedgerEntry: properties: - fax_job_id: + id: type: string - title: Fax Job Id - state: + title: Id + txn_id: type: string - title: State - failure_code: + title: Txn Id + account: + type: string + title: Account + amount_micro: + type: integer + title: Amount Micro + kind: + type: string + title: Kind + ref_type: anyOf: - type: string - type: 'null' - title: Failure Code - to: - type: string - title: To - from: + title: Ref Type + ref_id: anyOf: - type: string - type: 'null' - title: From - document_sha256: - type: string - title: Document Sha256 - transmitted_sha256: + title: Ref Id + memo: anyOf: - type: string - type: 'null' - title: Transmitted Sha256 - billable_pages: - type: integer - title: Billable Pages - pages_sent: - type: integer - title: Pages Sent - amount_micro: - type: integer - title: Amount Micro - captured_micro: - type: integer - title: Captured Micro - currency: - type: string - title: Currency - provider: - type: string - title: Provider + title: Memo created_at: type: string title: Created At - completed_at: - anyOf: - - type: string - - type: 'null' - title: Completed At - evidence: - anyOf: - - additionalProperties: true - type: object - - type: 'null' - title: Evidence - attempts: - items: - $ref: '#/components/schemas/Attempt' - type: array - title: Attempts - note: - type: string - title: Note - generated_at: - type: string - title: Generated At additionalProperties: true type: object required: - - fax_job_id - - state - - failure_code - - to - - document_sha256 - - transmitted_sha256 - - billable_pages - - pages_sent + - id + - txn_id + - account - amount_micro - - captured_micro - - currency - - provider + - kind + - ref_type + - ref_id + - memo - created_at - - completed_at - - evidence - - attempts - - note - - generated_at - title: Receipt - ReleaseRequest: + title: LedgerEntry + LedgerPage: properties: - confirm: + items: + items: + $ref: '#/components/schemas/LedgerEntry' + type: array + title: Items + next_cursor: anyOf: - type: string - type: 'null' - title: Confirm - description: The number itself, typed out, to confirm. - additionalProperties: false - type: object - title: ReleaseRequest - required: [] - Requested: - properties: - requested: - type: boolean - title: Requested - kind: - type: string - enum: - - export - - deletion - title: Kind - message: - type: string - title: Message + title: Next Cursor additionalProperties: true type: object required: - - requested - - kind - - message - title: Requested - SandboxFixture: + - items + title: LedgerPage + MagicLinkRequest: properties: - id: - type: string - title: Id - scenario: + email: type: string - title: Scenario - destination: + format: email + title: Email + next: anyOf: - type: string + maxLength: 400 - type: 'null' - title: Destination - created_at: - type: string - title: Created At - expires_at: - type: string - title: Expires At - additionalProperties: true - type: object - required: - - id - - scenario - - destination - - created_at - - expires_at - title: SandboxFixture - SandboxInboundCreate: - properties: - to: - type: string - title: To - from: - anyOf: - - type: string - - type: 'null' - title: From - pages: - type: integer - maximum: 200.0 - minimum: 1.0 - title: Pages - default: 1 - partial_content: - type: boolean - title: Partial Content - description: Rehearse a cut-off transmission. - default: false + title: Next + description: An in-app path to open after sign-in. additionalProperties: false type: object required: - - to - title: SandboxInboundCreate - SandboxReset: + - email + title: MagicLinkRequest + Me: properties: - reset: - type: boolean - title: Reset - deleted: - additionalProperties: - type: integer - type: object - title: Deleted - wallet_available_micro: - type: integer - title: Wallet Available Micro + org_id: + type: string + title: Org Id + project_id: + type: string + title: Project Id + key_id: + type: string + title: Key Id + key_prefix: + type: string + title: Key Prefix + environment: + type: string + enum: + - live + - test + title: Environment + scopes: + items: + type: string + type: array + title: Scopes + actor: + type: string + title: Actor additionalProperties: true type: object required: - - reset - - deleted - - wallet_available_micro - title: SandboxReset - SandboxScenarioCreate: + - org_id + - project_id + - key_id + - key_prefix + - environment + - scopes + - actor + title: Me + Member: properties: - scenario: + id: type: string - title: Scenario - destination: + title: Id + email: + type: string + title: Email + name: anyOf: - type: string - type: 'null' - title: Destination - description: Apply to one destination; omit for every send. - additionalProperties: false + title: Name + role: + type: string + enum: + - owner + - admin + - member + - viewer + title: Role + user_id: + anyOf: + - type: string + - type: 'null' + title: User Id + created_at: + type: string + title: Created At + additionalProperties: true type: object required: - - scenario - title: SandboxScenarioCreate - SandboxScenarios: + - id + - email + - name + - role + - user_id + - created_at + title: Member + MemberList: properties: - scenarios: - items: - additionalProperties: - type: string - type: object - type: array - title: Scenarios - destinations: - items: - additionalProperties: - type: string - type: object - type: array - title: Destinations - fixtures: + items: items: - $ref: '#/components/schemas/SandboxFixture' + $ref: '#/components/schemas/Member' type: array - title: Fixtures + title: Items additionalProperties: true type: object required: - - scenarios - - destinations - - fixtures - title: SandboxScenarios - SchemaCreate: + - items + title: MemberList + MemberPatch: properties: - name: - type: string - maxLength: 120 - minLength: 1 - title: Name - description: + role: anyOf: - type: string - maxLength: 500 + enum: + - owner + - admin + - member + - viewer - type: 'null' - title: Description - schema: - additionalProperties: true - type: object - title: Schema + title: Role + description: Required. Optional in the schema so a foreign id is a 404 first. additionalProperties: false type: object - required: - - name - - schema - title: SchemaCreate - SenderVerification: + title: MemberPatch + Number: properties: id: type: string @@ -12395,913 +13387,1184 @@ components: - live - test title: Environment - from: + e164: type: string - title: From + title: E164 + country: + type: string + title: Country + number_type: + type: string + title: Number Type + locality: + anyOf: + - type: string + - type: 'null' + title: Locality status: type: string title: Status - method: + label: + anyOf: + - type: string + - type: 'null' + title: Label + project_id: + anyOf: + - type: string + - type: 'null' + title: Project Id + inbound_webhook_id: + anyOf: + - type: string + - type: 'null' + title: Inbound Webhook Id + inbound_status: type: string - title: Method - number_id: + title: Inbound Status + provider: + type: string + title: Provider + monthly_micro: + type: integer + title: Monthly Micro + currency: + type: string + title: Currency + order_id: anyOf: - type: string - type: 'null' - title: Number Id + title: Order Id created_at: type: string title: Created At - verified_at: + activated_at: anyOf: - type: string - type: 'null' - title: Verified At + title: Activated At + renews_at: + anyOf: + - type: string + - type: 'null' + title: Renews At + released_at: + anyOf: + - type: string + - type: 'null' + title: Released At additionalProperties: true type: object required: - id - environment - - from + - e164 + - country + - number_type + - locality - status - - method - - number_id - - created_at - - verified_at - title: SenderVerification - SenderVerificationCreate: - properties: - from: - type: string - title: From - description: A number your organization owns, E.164. - additionalProperties: false + - label + - project_id + - inbound_webhook_id + - inbound_status + - provider + - monthly_micro + - currency + - order_id + - created_at + - activated_at + - renews_at + - released_at + title: Number + NumberList: + properties: + items: + items: + $ref: '#/components/schemas/Number' + type: array + title: Items + additionalProperties: true type: object required: - - from - title: SenderVerificationCreate - ServiceStatus: + - items + title: NumberList + NumberOrder: properties: - status: - type: string - enum: - - ok - - degraded - title: Status - version: + id: type: string - title: Version + title: Id environment: type: string enum: - live - test title: Environment - provider: + status: type: string - title: Provider - database: - type: boolean - title: Database - worker_beat_age_s: + title: Status + e164: anyOf: - - type: integer + - type: string - type: 'null' - title: Worker Beat Age S - status_page: + title: E164 + country: anyOf: - type: string - type: 'null' - title: Status Page - additionalProperties: true - type: object - required: - - status - - version - - environment - - provider - - database - title: ServiceStatus - SignedUrl: - properties: - url: - type: string - title: Url - expires_at: - type: string - title: Expires At - content_type: - type: string - title: Content Type - additionalProperties: true - type: object - required: - - url - - expires_at - - content_type - title: SignedUrl - Subscription: - properties: - id: - type: string - title: Id - product: - type: string - title: Product - status: - type: string - title: Status - stripe_subscription_id: - type: string - title: Stripe Subscription Id - stripe_price_id: + title: Country + number_type: anyOf: - type: string - type: 'null' - title: Stripe Price Id - cancel_at_period_end: - type: boolean - title: Cancel At Period End - current_period_end: + title: Number Type + number_id: anyOf: - type: string - type: 'null' - title: Current Period End - entitled: - type: boolean - title: Entitled + title: Number Id + number: + anyOf: + - $ref: '#/components/schemas/Number' + - type: 'null' + provider: + type: string + title: Provider + recurring_charge_micro: + type: integer + title: Recurring Charge Micro + description: Charged every month on the activation day. + recurring_period: + type: string + title: Recurring Period + first_month_hold_micro: + type: integer + title: First Month Hold Micro + description: Held now, captured when the number activates. + currency: + type: string + title: Currency + error: + anyOf: + - type: string + - type: 'null' + title: Error created_at: type: string title: Created At - updated_at: - type: string - title: Updated At - canceled_at: + completed_at: anyOf: - type: string - type: 'null' - title: Canceled At + title: Completed At additionalProperties: true type: object required: - id - - product + - environment - status - - stripe_subscription_id - - stripe_price_id - - cancel_at_period_end - - current_period_end - - entitled + - e164 + - country + - number_type + - number_id + - number + - provider + - recurring_charge_micro + - recurring_period + - first_month_hold_micro + - currency + - error - created_at - - updated_at - - canceled_at - title: Subscription - SubscriptionCheckout: + - completed_at + title: NumberOrder + NumberOrderCreate: properties: - url: - type: string - title: Url - stripe_session_id: - type: string - title: Stripe Session Id - product: + e164: type: string - title: Product - additionalProperties: true + title: E164 + description: A number from GET /v1/number-inventory, E.164. + label: + anyOf: + - type: string + maxLength: 120 + - type: 'null' + title: Label + project_id: + anyOf: + - type: string + - type: 'null' + title: Project Id + description: Inbound faxes are filed under this project. + inbound_webhook_id: + anyOf: + - type: string + - type: 'null' + title: Inbound Webhook Id + description: 'The webhook that receives fax.received for this number (default: all).' + additionalProperties: false type: object required: - - url - - stripe_session_id - - product - title: SubscriptionCheckout - SubscriptionList: + - e164 + title: NumberOrderCreate + NumberOrderList: properties: items: items: - $ref: '#/components/schemas/Subscription' + $ref: '#/components/schemas/NumberOrder' type: array title: Items - workspace: - type: boolean - title: Workspace - next_cursor: - anyOf: - - type: string - - type: 'null' - title: Next Cursor additionalProperties: true type: object required: - items - - workspace - title: SubscriptionList - SummaryCreate: + title: NumberOrderList + NumberUpdate: properties: - inbox_item_id: + label: anyOf: - type: string - pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ + maxLength: 120 - type: 'null' - title: Inbox Item Id - document_id: + title: Label + project_id: anyOf: - type: string - pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ - type: 'null' - title: Document Id - max_pages: + title: Project Id + inbound_webhook_id: anyOf: - - type: integer - maximum: 200.0 - minimum: 1.0 + - type: string - type: 'null' - title: Max Pages - strict: - type: boolean - title: Strict - description: Refuse with page_limit_exceeded when the document has more pages than would be read, instead - of reading the first ones and warning. - default: false - strong_model_if_below: - type: number - maximum: 1.0 - minimum: 0.0 - title: Strong Model If Below - default: 0.75 - additionalProperties: false - type: object - title: SummaryCreate - required: [] - TopupCreate: - properties: - amount_usd: - type: integer - maximum: 1000.0 - minimum: 10.0 - title: Amount Usd - description: Whole US dollars, 10 to 1000 per session. + title: Inbound Webhook Id additionalProperties: false type: object - required: - - amount_usd - title: TopupCreate - TopupSession: + title: NumberUpdate + OAuthConsent: properties: id: type: string title: Id - environment: + client: + $ref: '#/components/schemas/OAuthConsentClient' + redirect_host: + type: string + title: Redirect Host + scopes_requested: + items: + type: string + type: array + title: Scopes Requested + organizations: + items: + $ref: '#/components/schemas/OAuthConsentOrganization' + type: array + title: Organizations + default_environment: type: string enum: - live - test - title: Environment - kind: - type: string - enum: - - manual - - auto - - setup - title: Kind - amount_micro: - type: integer - title: Amount Micro - currency: - type: string - title: Currency - status: - type: string - enum: - - open - - completed - - expired - - canceled - - failed - title: Status - url: - anyOf: - - type: string - - type: 'null' - title: Url - description: The hosted Checkout page while the session is open. - stripe_session_id: - anyOf: - - type: string - - type: 'null' - title: Stripe Session Id - stripe_payment_intent_id: - anyOf: - - type: string - - type: 'null' - title: Stripe Payment Intent Id - auto_topup_id: - anyOf: - - type: string - - type: 'null' - title: Auto Topup Id - error: - anyOf: - - type: string - - type: 'null' - title: Error - created_at: - type: string - title: Created At + title: Default Environment expires_at: - anyOf: - - type: string - - type: 'null' + type: string title: Expires At - completed_at: - anyOf: - - type: string - - type: 'null' - title: Completed At additionalProperties: true type: object required: - id - - environment - - kind - - amount_micro - - currency - - status - - stripe_session_id - - stripe_payment_intent_id - - auto_topup_id - - error - - created_at + - client + - redirect_host + - scopes_requested + - organizations + - default_environment - expires_at - - completed_at - title: TopupSession - TopupSessionPage: + title: OAuthConsent + OAuthConsentClient: properties: - items: - items: - $ref: '#/components/schemas/TopupSession' - type: array - title: Items - next_cursor: + name: anyOf: - type: string - type: 'null' - title: Next Cursor + title: Name + description: The client's own name, shown as text beside the host. + host: + type: string + title: Host + description: 'The host the person is sending a credential to: the trust anchor.' + loopback: + type: boolean + title: Loopback + description: The redirect goes to the person's own computer (an app they started). + first_seen: + type: boolean + title: First Seen + description: This client was registered during this request. additionalProperties: true type: object required: - - items - title: TopupSessionPage - UploadComplete: - properties: - sha256: - anyOf: - - type: string - maxLength: 64 - minLength: 64 - - type: 'null' - title: Sha256 - description: 'Optional: fail if the bytes differ.' - additionalProperties: false - type: object - title: UploadComplete - required: [] - UploadCreate: + - name + - host + - loopback + - first_seen + title: OAuthConsentClient + OAuthConsentDecision: properties: - filename: + org_id: type: string - maxLength: 255 + maxLength: 64 minLength: 1 - title: Filename - content_type: + title: Org Id + environment: type: string - maxLength: 100 - title: Content Type - description: application/pdf, image/png, image/jpeg or image/tiff. An image becomes 8-bit gray letter - pages, since fax is monochrome. A frame wider than it is tall is rotated. A frame above 300 dpi letter - is downsampled. Every TIFF frame is a page. PNG and JPEG take the first frame. The bytes must be the - declared type. - default: application/pdf - size_bytes: - type: integer - minimum: 1.0 - title: Size Bytes + enum: + - live + - test + title: Environment + default: test additionalProperties: false type: object required: - - filename - - size_bytes - title: UploadCreate - UploadSession: + - org_id + title: OAuthConsentDecision + OAuthConsentOrganization: properties: id: type: string title: Id - status: + name: type: string - title: Status - filename: + title: Name + role: type: string - title: Filename - content_type: + title: Role + scopes: + items: + type: string + type: array + title: Scopes + description: What the grant would carry for this organization and role. + additionalProperties: true + type: object + required: + - id + - name + - role + - scopes + title: OAuthConsentOrganization + OAuthRedirect: + properties: + redirect_to: type: string - title: Content Type - declared_size: - type: integer - title: Declared Size - received_size: - type: integer - title: Received Size - sha256: + title: Redirect To + description: 'Where the browser goes next: the client''s registered redirect URI.' + additionalProperties: true + type: object + required: + - redirect_to + title: OAuthRedirect + OneTimeCard: + properties: + last4: anyOf: - type: string - type: 'null' - title: Sha256 - created_at: + title: Last4 + brand: + anyOf: + - type: string + - type: 'null' + title: Brand + additionalProperties: true + type: object + required: + - last4 + - brand + title: OneTimeCard + OneTimeCheckout: + properties: + order_id: type: string - title: Created At + title: Order Id + checkout_url: + type: string + title: Checkout Url expires_at: type: string title: Expires At - completed_at: - anyOf: - - type: string - - type: 'null' - title: Completed At - upload_url: - anyOf: - - type: string - - type: 'null' - title: Upload Url - upload_method: - anyOf: - - type: string - - type: 'null' - title: Upload Method - upload_url_expires_at: + amount_micro: + type: integer + title: Amount Micro + currency: + type: string + title: Currency + additionalProperties: true + type: object + required: + - order_id + - checkout_url + - expires_at + - amount_micro + - currency + title: OneTimeCheckout + OneTimeConfig: + properties: + enabled: + type: boolean + title: Enabled + reason: anyOf: - type: string - type: 'null' - title: Upload Url Expires At + title: Reason + price_version: + type: string + title: Price Version + effective_from: + type: string + title: Effective From + currency: + type: string + title: Currency + routes: + additionalProperties: + $ref: '#/components/schemas/OneTimeRoute' + type: object + title: Routes + max_document_pages: + type: integer + title: Max Document Pages + max_upload_bytes: + type: integer + title: Max Upload Bytes + max_text_chars: + type: integer + title: Max Text Chars + formats: + items: + type: string + type: array + title: Formats + aup_version: + type: string + title: Aup Version + aup_url: + type: string + title: Aup Url + terms_url: + type: string + title: Terms Url additionalProperties: true type: object required: - - id - - status - - filename - - content_type - - declared_size - - received_size - - sha256 - - created_at - - expires_at - - completed_at - title: UploadSession - UsageExport: + - enabled + - price_version + - effective_from + - currency + - routes + - max_document_pages + - max_upload_bytes + - max_text_chars + - formats + - aup_version + - aup_url + - terms_url + title: OneTimeConfig + description: |- + What the public send page and an agent read before an order: whether the product is open here, the + card the order will be priced from (present only while open), the document limits and the policy the + sender accepts. Prices are integer micro-USD; the dollar figure is the client's to format. + OneTimeConfirm: properties: - id: + token: type: string - title: Id - environment: + maxLength: 128 + minLength: 1 + title: Token + description: The token of the link in the confirmation mail. + additionalProperties: false + type: object + required: + - token + title: OneTimeConfirm + OneTimeConfirmSummary: + properties: + order_id: + type: string + title: Order Id + state: type: string enum: - - live - - test - title: Environment + - ok + - link_used + - link_expired + title: State status: type: string enum: - - ready + - draft + - checkout_open + - authorized + - awaiting_confirmation + - released + - capturing + - canceling + - captured + - released_hold - expired + - refused + - settle_failed title: Status - params: - additionalProperties: true - type: object - title: Params - row_count: + to: + type: string + title: To + sender_name: + type: string + title: Sender Name + email: + type: string + title: Email + document_pages: type: integer - title: Row Count - content_type: + title: Document Pages + amount_micro: + type: integer + title: Amount Micro + currency: type: string - title: Content Type - url: + title: Currency + confirm_deadline_at: anyOf: - type: string - type: 'null' - title: Url - created_at: - type: string - title: Created At - expires_at: - type: string - title: Expires At + title: Confirm Deadline At additionalProperties: true type: object required: - - id - - environment + - order_id + - state - status - - params - - row_count - - content_type - - url - - created_at - - expires_at - title: UsageExport - UsageExportCreate: + - to + - sender_name + - email + - document_pages + - amount_micro + - currency + - confirm_deadline_at + title: OneTimeConfirmSummary + description: |- + What the confirm page shows before the click. ``state`` is ``ok`` while the link can still be used, + else ``link_used`` or ``link_expired``. Reading it consumes nothing. + OneTimeConfirmed: properties: - from: - anyOf: - - type: string - - type: 'null' - title: From - description: ISO date; default 30 days ago. - to: + order: + $ref: '#/components/schemas/OneTimeOrder' + signed_in: + type: boolean + title: Signed In + me: anyOf: - - type: string + - additionalProperties: true + type: object - type: 'null' - title: To - description: ISO date; default today. - additionalProperties: false + title: Me + additionalProperties: true type: object - title: UsageExportCreate - required: [] - UsageItem: + required: + - order + - signed_in + title: OneTimeConfirmed + description: |- + ``signed_in`` says whether this browser now holds a session for the payer; ``me`` is then the same + object ``GET /v1/auth/me`` answers (the CSRF token included). + OneTimeFromQuote: properties: - day: + quote_id: + type: string + title: Quote Id + sender_name: + type: string + maxLength: 200 + title: Sender Name + description: Printed on the cover sheet. 2 to 120 characters. + accept_aup: + type: boolean + title: Accept Aup + email: anyOf: - type: string + maxLength: 254 - type: 'null' - title: Day - direction: + title: Email + approval_id: anyOf: - type: string + maxLength: 64 - type: 'null' - title: Direction - route: + title: Approval Id + description: An approved, unused approval request for this quote (POST /v1/approval-requests). A key needs + it unless a delegated policy allows the send. A session needs none. + source_ref: anyOf: - type: string + maxLength: 32 - type: 'null' - title: Route - destination: + title: Source Ref + delete_after_send: anyOf: - - type: string + - type: boolean - type: 'null' - title: Destination - project_id: + title: Delete After Send + description: Delete the document and the transmitted copy the moment the fax ends in delivered, partial + or failed. The order, the receipt and the carrier evidence stay; they hold no document content. A fax + that ends unknown keeps the document until the carrier reports, or until the retention period ends. + Left out, the quote's own delete_after_send applies. + additionalProperties: false + type: object + required: + - quote_id + - sender_name + - accept_aup + title: OneTimeFromQuote + OneTimeJob: + properties: + order_id: + type: string + title: Order Id + status: anyOf: - type: string + enum: + - draft + - checkout_open + - authorized + - awaiting_confirmation + - released + - capturing + - canceling + - captured + - released_hold + - expired + - refused + - settle_failed - type: 'null' - title: Project Id - pages: - type: integer - title: Pages - jobs: - type: integer - title: Jobs + title: Status amount_micro: - type: integer + anyOf: + - type: integer + - type: 'null' title: Amount Micro + captured_micro: + anyOf: + - type: integer + - type: 'null' + title: Captured Micro + refunded_micro: + anyOf: + - type: integer + - type: 'null' + title: Refunded Micro + description: What was refunded to the card after the charge, in micro-units. + document_pages: + anyOf: + - type: integer + - type: 'null' + title: Document Pages + description: The pages of the order's document. The cover sheet is not counted. + card: + anyOf: + - $ref: '#/components/schemas/OneTimeCard' + - type: 'null' additionalProperties: true type: object required: - - pages - - jobs - - amount_micro - title: UsageItem - UsageReport: + - order_id + title: OneTimeJob + description: 'The order behind a fax job that a one-time send made: what was authorized, what was charged.' + OneTimeOrder: properties: - environment: + id: type: string - enum: + title: Id + status: + type: string + enum: + - draft + - checkout_open + - authorized + - awaiting_confirmation + - released + - capturing + - canceling + - captured + - released_hold + - expired + - refused + - settle_failed + title: Status + next: + type: string + enum: + - checkout + - confirm + - wait + - done + title: Next + environment: + type: string + enum: - live - test title: Environment - from: - type: string - title: From - to: + source: type: string - title: To - group_by: - items: - type: string - type: array - title: Group By - currency: + enum: + - web + - api + - mcp + title: Source + email: type: string - title: Currency - items: - items: - $ref: '#/components/schemas/UsageItem' - type: array - title: Items - totals: - additionalProperties: - type: integer - type: object - title: Totals - additionalProperties: true - type: object - required: - - environment - - from - - to - - group_by - - currency - - items - - totals - title: UsageReport - ValidationError: - properties: - loc: - items: - anyOf: - - type: string - - type: integer - type: array - title: Location - msg: + title: Email + email_verified: + type: boolean + title: Email Verified + sender_name: type: string - title: Message - type: + title: Sender Name + to: type: string - title: Error Type - input: - title: Input - ctx: - type: object - title: Context - type: object - required: - - loc - - msg - - type - title: ValidationError - Webhook: - properties: - id: + title: To + route: type: string - title: Id - url: + title: Route + cover: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Cover + document_pages: + type: integer + title: Document Pages + document_sha256: type: string - title: Url - description: + title: Document Sha256 + document_id: anyOf: - type: string - type: 'null' - title: Description - event_types: - items: - type: string - type: array - title: Event Types - status: - type: string - title: Status - previous_secret_expires_at: + title: Document Id + delete_after_send: + type: boolean + title: Delete After Send + description: The sender asked for the document and the transmitted copy to be deleted the moment the fax + ends in delivered, partial or failed. + default: false + document_deleted: + type: boolean + title: Document Deleted + description: 'True once no copy of the document is held: it was deleted after the send, by the retention + period or by its owner, or the order ended before it was released and the uploaded file was removed. + The order, the fax and the carrier evidence stay; they hold no document content.' + default: false + document_expires_at: anyOf: - type: string - type: 'null' - title: Previous Secret Expires At - created_at: - type: string - title: Created At - updated_at: - type: string - title: Updated At - additionalProperties: true - type: object - required: - - id - - url - - description - - event_types - - status - - previous_secret_expires_at - - created_at - - updated_at - title: Webhook - WebhookCreate: - properties: - url: - type: string - maxLength: 2000 - title: Url - description: + title: Document Expires At + description: When the retention period deletes the stored document. Null before the release and once the + document is deleted. + price: + $ref: '#/components/schemas/OneTimePrice' + amount_micro: + type: integer + title: Amount Micro + captured_micro: + type: integer + title: Captured Micro + refunded_micro: + type: integer + title: Refunded Micro + description: What was refunded to the card after the charge, in micro-units, as the ledger holds it. Zero + when nothing was refunded. captured_micro stays what was charged. + default: 0 + card: + anyOf: + - $ref: '#/components/schemas/OneTimeCard' + - type: 'null' + checkout_url: anyOf: - type: string - maxLength: 200 - type: 'null' - title: Description - event_types: + title: Checkout Url + checkout_expires_at: anyOf: - - items: - type: string - type: array + - type: string - type: 'null' - title: Event Types - description: 'Empty or omitted: every event type.' - additionalProperties: false - type: object - required: - - url - title: WebhookCreate - WebhookList: - properties: - items: - items: - $ref: '#/components/schemas/Webhook' - type: array - title: Items - additionalProperties: true - type: object - required: - - items - title: WebhookList - WebhookUpdate: - properties: - url: + title: Checkout Expires At + authorized_at: anyOf: - type: string - maxLength: 2000 - type: 'null' - title: Url - description: + title: Authorized At + confirm_deadline_at: anyOf: - type: string - maxLength: 200 - type: 'null' - title: Description - event_types: + title: Confirm Deadline At + capture_deadline_at: anyOf: - - items: - type: string - type: array + - type: string - type: 'null' - title: Event Types - status: + title: Capture Deadline At + confirm_mail_sent_at: anyOf: - type: string - enum: - - active - - paused - type: 'null' - title: Status - additionalProperties: false - type: object - title: WebhookUpdate - required: [] - WebhookWithSecret: - properties: - id: - type: string - title: Id - url: - type: string - title: Url - description: + title: Confirm Mail Sent At + resend_allowed_at: anyOf: - type: string - type: 'null' - title: Description - event_types: + title: Resend Allowed At + description: When the confirmation mail may be asked for again; null when it may not. + released_at: + anyOf: + - type: string + - type: 'null' + title: Released At + settled_at: + anyOf: + - type: string + - type: 'null' + title: Settled At + quote_id: + anyOf: + - type: string + - type: 'null' + title: Quote Id + fax_job_id: + anyOf: + - type: string + - type: 'null' + title: Fax Job Id + failure_code: + anyOf: + - type: string + - type: 'null' + title: Failure Code + aup_version: + type: string + title: Aup Version + warnings: items: - type: string + additionalProperties: true + type: object type: array - title: Event Types - status: - type: string - title: Status - previous_secret_expires_at: + title: Warnings + fax: anyOf: - - type: string + - additionalProperties: true + type: object - type: 'null' - title: Previous Secret Expires At + title: Fax + description: The fax job (the FaxJob shape) once the order was released; null before. created_at: type: string title: Created At updated_at: type: string title: Updated At - secret: - type: string - title: Secret - description: whsec_... Shown once. Verify webhook-signature with it. additionalProperties: true type: object required: - id - - url - - description - - event_types - status - - previous_secret_expires_at + - next + - environment + - source + - email + - email_verified + - sender_name + - to + - route + - cover + - document_pages + - document_sha256 + - document_id + - price + - amount_micro + - captured_micro + - card + - checkout_url + - checkout_expires_at + - authorized_at + - confirm_deadline_at + - capture_deadline_at + - confirm_mail_sent_at + - released_at + - settled_at + - quote_id + - fax_job_id + - failure_code + - aup_version - created_at - updated_at - - secret - title: WebhookWithSecret - ErrorEnvelope: - title: ErrorEnvelope - type: object - required: - - error - properties: - error: - type: object - required: - - code - - message - - retryable - - correlation_id - properties: - code: - type: string - description: A stable code such as invalid_token, scope_missing, not_found, quote_expired, insufficient_balance, - approval_required, rate_limited, live_provider_unavailable. - message: - type: string - description: One sentence a person can act on. - retryable: - type: boolean - correlation_id: - type: string - description: The X-Request-Id of the request. Quote it when you write to hello@faxforagents.com. - details: - type: object - additionalProperties: true - description: Code-specific fields, for example details.scope on scope_missing. - AbuseReview: + title: OneTimeOrder + description: |- + One order of the one-time send. ``next`` is what the caller does now: ``checkout`` (open the + payment), ``confirm`` (the payer clicks the link in the mail), ``wait`` (nothing to do; read again) or + ``done``. Money is integer micro-USD. ``captured_micro`` is what the card was charged, written only + once the payment provider confirmed it. ``fax`` is the fax job once the order was released, with the + carrier's evidence once it has any. + OneTimeOrderCreated: properties: id: type: string title: Id - org_id: - type: string - title: Org Id - key_id: - anyOf: - - type: string - - type: 'null' - title: Key Id status: type: string enum: - - open - - resolved + - draft + - checkout_open + - authorized + - awaiting_confirmation + - released + - capturing + - canceling + - captured + - released_hold + - expired + - refused + - settle_failed title: Status - action: + next: type: string enum: - - none - - suspended - title: Action - opened_by: + - checkout + - confirm + - wait + - done + title: Next + environment: type: string - title: Opened By - additionalProperties: true - type: object - required: - - id - - org_id - - status - - action - - opened_by - title: AbuseReview - AbuseReviewCreate: - properties: - org_id: + enum: + - live + - test + title: Environment + source: type: string - maxLength: 64 - minLength: 3 - title: Org Id - key_id: + enum: + - web + - api + - mcp + title: Source + email: + type: string + title: Email + email_verified: + type: boolean + title: Email Verified + sender_name: + type: string + title: Sender Name + to: + type: string + title: To + route: + type: string + title: Route + cover: anyOf: - - type: string - maxLength: 64 + - additionalProperties: true + type: object - type: 'null' - title: Key Id - reason: + title: Cover + document_pages: + type: integer + title: Document Pages + document_sha256: type: string - maxLength: 500 - minLength: 3 - title: Reason - description: 'Why: recorded on the audit row' - suspend: + title: Document Sha256 + document_id: + anyOf: + - type: string + - type: 'null' + title: Document Id + delete_after_send: type: boolean - title: Suspend - description: Suspend sends for the organization or key at once + title: Delete After Send + description: The sender asked for the document and the transmitted copy to be deleted the moment the fax + ends in delivered, partial or failed. default: false - additionalProperties: false + document_deleted: + type: boolean + title: Document Deleted + description: 'True once no copy of the document is held: it was deleted after the send, by the retention + period or by its owner, or the order ended before it was released and the uploaded file was removed. + The order, the fax and the carrier evidence stay; they hold no document content.' + default: false + document_expires_at: + anyOf: + - type: string + - type: 'null' + title: Document Expires At + description: When the retention period deletes the stored document. Null before the release and once the + document is deleted. + price: + $ref: '#/components/schemas/OneTimePrice' + amount_micro: + type: integer + title: Amount Micro + captured_micro: + type: integer + title: Captured Micro + refunded_micro: + type: integer + title: Refunded Micro + description: What was refunded to the card after the charge, in micro-units, as the ledger holds it. Zero + when nothing was refunded. captured_micro stays what was charged. + default: 0 + card: + anyOf: + - $ref: '#/components/schemas/OneTimeCard' + - type: 'null' + checkout_url: + anyOf: + - type: string + - type: 'null' + title: Checkout Url + checkout_expires_at: + anyOf: + - type: string + - type: 'null' + title: Checkout Expires At + authorized_at: + anyOf: + - type: string + - type: 'null' + title: Authorized At + confirm_deadline_at: + anyOf: + - type: string + - type: 'null' + title: Confirm Deadline At + capture_deadline_at: + anyOf: + - type: string + - type: 'null' + title: Capture Deadline At + confirm_mail_sent_at: + anyOf: + - type: string + - type: 'null' + title: Confirm Mail Sent At + resend_allowed_at: + anyOf: + - type: string + - type: 'null' + title: Resend Allowed At + description: When the confirmation mail may be asked for again; null when it may not. + released_at: + anyOf: + - type: string + - type: 'null' + title: Released At + settled_at: + anyOf: + - type: string + - type: 'null' + title: Settled At + quote_id: + anyOf: + - type: string + - type: 'null' + title: Quote Id + fax_job_id: + anyOf: + - type: string + - type: 'null' + title: Fax Job Id + failure_code: + anyOf: + - type: string + - type: 'null' + title: Failure Code + aup_version: + type: string + title: Aup Version + warnings: + items: + additionalProperties: true + type: object + type: array + title: Warnings + fax: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Fax + description: The fax job (the FaxJob shape) once the order was released; null before. + created_at: + type: string + title: Created At + updated_at: + type: string + title: Updated At + status_token: + type: string + title: Status Token + status_url: + type: string + title: Status Url + additionalProperties: true type: object required: - - org_id - - reason - title: AbuseReviewCreate - AbuseReviewPage: + - id + - status + - next + - environment + - source + - email + - email_verified + - sender_name + - to + - route + - cover + - document_pages + - document_sha256 + - document_id + - price + - amount_micro + - captured_micro + - card + - checkout_url + - checkout_expires_at + - authorized_at + - confirm_deadline_at + - capture_deadline_at + - confirm_mail_sent_at + - released_at + - settled_at + - quote_id + - fax_job_id + - failure_code + - aup_version + - created_at + - updated_at + - status_token + - status_url + title: OneTimeOrderCreated + description: |- + The create answer: the order plus the status token. The token reads this one order, opens its + checkout and asks for the confirmation mail again, with no key and no session, for seven days. Send it + as ``X-FFA-Order-Token`` (or ``?t=``). ``status_url`` is the order's page in the app with the token. + OneTimeOrderPage: properties: items: items: - $ref: '#/components/schemas/AbuseReview' + $ref: '#/components/schemas/OneTimeOrder' type: array title: Items next_cursor: @@ -13313,144 +14576,162 @@ components: type: object required: - items - title: AbuseReviewPage - AbuseReviewPatch: + title: OneTimeOrderPage + OneTimePrice: properties: - action: + price_version: type: string - enum: - - suspend - - unsuspend - - resolve - title: Action - reason: + title: Price Version + currency: type: string - maxLength: 500 - minLength: 3 - title: Reason - description: 'Why: recorded on the audit row' - additionalProperties: false + title: Currency + flat_micro: + type: integer + title: Flat Micro + included_pages: + type: integer + title: Included Pages + extra_page_micro: + type: integer + title: Extra Page Micro + cover_included: + type: boolean + title: Cover Included + additionalProperties: true type: object required: - - action - - reason - title: AbuseReviewPatch - Accepted: + - price_version + - currency + - flat_micro + - included_pages + - extra_page_micro + - cover_included + title: OneTimePrice + OneTimeRoute: properties: - organization: - $ref: '#/components/schemas/OrganizationSummary' + label: + type: string + title: Label + flat_micro: + type: integer + title: Flat Micro + included_pages: + type: integer + title: Included Pages + extra_page_micro: + type: integer + title: Extra Page Micro + cover_included: + type: boolean + title: Cover Included additionalProperties: true type: object required: - - organization - title: Accepted - Adjustment: + - label + - flat_micro + - included_pages + - extra_page_micro + - cover_included + title: OneTimeRoute + Organization: properties: id: type: string title: Id - org_id: - type: string - title: Org Id - environment: - type: string - title: Environment - direction: - type: string - title: Direction - amount_micro: - type: integer - title: Amount Micro - status: - type: string - title: Status - proposed_by: + name: type: string - title: Proposed By - applied_by: - anyOf: - - type: string - - type: 'null' - title: Applied By - txn_id: + title: Name + role: anyOf: - type: string + enum: + - owner + - admin + - member + - viewer - type: 'null' - title: Txn Id + title: Role + created_at: + type: string + title: Created At + members: + type: integer + title: Members additionalProperties: true type: object required: - id - - org_id - - environment - - direction - - amount_micro - - status - - proposed_by - title: Adjustment - AdjustmentApply: + - name + - role + - created_at + - members + title: Organization + OrganizationList: properties: - reason: + items: + items: + $ref: '#/components/schemas/OrganizationSummary' + type: array + title: Items + additionalProperties: true + type: object + required: + - items + title: OrganizationList + OrganizationPatch: + properties: + name: type: string - maxLength: 500 - minLength: 3 - title: Reason - description: 'Why: recorded on the audit row' - confirm: - type: boolean - title: Confirm - description: Required when the proposer applies their own adjustment - default: false + maxLength: 200 + minLength: 1 + title: Name additionalProperties: false type: object required: - - reason - title: AdjustmentApply - AdjustmentCreate: + - name + title: OrganizationPatch + OrganizationSummary: properties: - org_id: + id: type: string - maxLength: 64 - minLength: 3 - title: Org Id - environment: + title: Id + name: type: string - enum: - - live - - test - title: Environment - default: live - direction: + title: Name + role: type: string enum: - - credit - - debit - title: Direction - amount_micro: - type: integer - maximum: 10000000000.0 - exclusiveMinimum: 0.0 - title: Amount Micro - description: Integer micro-USD; at most 10,000.00 - reason: + - owner + - admin + - member + - viewer + title: Role + additionalProperties: true + type: object + required: + - id + - name + - role + title: OrganizationSummary + OutboxDiscarded: + properties: + id: type: string - maxLength: 500 - minLength: 3 - title: Reason - description: 'Why: recorded on the audit row' - additionalProperties: false + title: Id + discarded: + type: boolean + title: Discarded + additionalProperties: true type: object required: - - org_id - - direction - - amount_micro - - reason - title: AdjustmentCreate - AdjustmentPage: + - id + - discarded + title: OutboxDiscarded + OutboxPage: properties: items: items: - $ref: '#/components/schemas/Adjustment' + $ref: '#/components/schemas/OutboxRow' type: array title: Items next_cursor: @@ -13462,187 +14743,178 @@ components: type: object required: - items - title: AdjustmentPage - AdminAction: + title: OutboxPage + OutboxRow: properties: - reason: + id: type: string - maxLength: 500 - minLength: 3 - title: Reason - description: 'Why: recorded on the audit row' - additionalProperties: false - type: object - required: - - reason - title: AdminAction - AdminBrief: - properties: - utc_day: - anyOf: - - type: string - - type: 'null' - title: Utc Day - text: + title: Id + kind: + type: string + title: Kind + status: + type: string + enum: + - queued + - running + - done + - failed + - dead + title: Status + title: anyOf: - type: string - type: 'null' - title: Text - posted_at: + title: Title + error: anyOf: - type: string - type: 'null' - title: Posted At - channel: - anyOf: - - type: string - - type: 'null' - title: Channel - status: - anyOf: - - type: string - enum: - - posted - - failed - - gave_up - - type: 'null' - title: Status + title: Error attempts: type: integer title: Attempts - default: 0 - local_day: - anyOf: - - type: string - - type: 'null' - title: Local Day - attempted_at: - anyOf: - - type: string - - type: 'null' - title: Attempted At - attempt_utc_day: + max_attempts: + type: integer + title: Max Attempts + org_id: anyOf: - type: string - type: 'null' - title: Attempt Utc Day - retry_after_s: - type: integer - title: Retry After S - default: 600 - waiting_day: + title: Org Id + created_at: + type: string + title: Created At + finished_at: anyOf: - type: string - type: 'null' - title: Waiting Day - additionalProperties: true - type: object - title: AdminBrief - AdminDaily: - properties: - metric: - type: string - title: Metric - description: - type: string - title: Description - days: + title: Finished At + age_s: type: integer - title: Days - items: - items: - $ref: '#/components/schemas/AdminDailyPoint' - type: array - title: Items + title: Age S additionalProperties: true type: object required: - - metric - - description - - days - - items - title: AdminDaily - AdminDailyPoint: + - id + - kind + - status + - attempts + - max_attempts + - created_at + - age_s + title: OutboxRow + PagePreview: properties: - day: - type: string - title: Day - environment: - type: string - title: Environment - dims: - additionalProperties: true - type: object - title: Dims - value_num: + page_number: + type: integer + title: Page Number + width_px: anyOf: - type: integer - type: 'null' - title: Value Num - value_json: - title: Value Json - computed_at: + title: Width Px + height_px: + anyOf: + - type: integer + - type: 'null' + title: Height Px + url: type: string - title: Computed At + title: Url additionalProperties: true type: object required: - - day - - environment - - dims - - computed_at - title: AdminDailyPoint - AdminJob: + - page_number + - width_px + - height_px + - url + title: PagePreview + Passkey: properties: id: type: string title: Id - org_id: - type: string - title: Org Id - state: - type: string - title: State - route: - type: string - title: Route - environment: + label: + anyOf: + - type: string + - type: 'null' + title: Label + aaguid: + anyOf: + - type: string + - type: 'null' + title: Aaguid + transports: + anyOf: + - items: + type: string + type: array + - type: 'null' + title: Transports + backup_eligible: + type: boolean + title: Backup Eligible + created_at: type: string - enum: - - live - - test - title: Environment - age_s: - type: integer - title: Age S + title: Created At + last_used_at: + anyOf: + - type: string + - type: 'null' + title: Last Used At additionalProperties: true type: object required: - id - - org_id - - state - - route - - environment - - age_s - title: AdminJob - AdminJobPage: + - label + - aaguid + - transports + - backup_eligible + - created_at + - last_used_at + title: Passkey + PasskeyList: properties: items: items: - $ref: '#/components/schemas/AdminJob' + $ref: '#/components/schemas/Passkey' type: array title: Items - next_cursor: + additionalProperties: true + type: object + required: + - items + title: PasskeyList + PasskeyLoginVerify: + properties: + credential: + additionalProperties: true + type: object + title: Credential + additionalProperties: false + type: object + required: + - credential + title: PasskeyLoginVerify + PasskeyRegisterVerify: + properties: + credential: + additionalProperties: true + type: object + title: Credential + label: anyOf: - type: string + maxLength: 80 - type: 'null' - title: Next Cursor - additionalProperties: true + title: Label + additionalProperties: false type: object required: - - items - title: AdminJobPage - AdminOrganization: + - credential + title: PasskeyRegisterVerify + Policy: properties: id: type: string @@ -13650,1358 +14922,1844 @@ components: name: type: string title: Name - suspended_at: + purpose: anyOf: - type: string - type: 'null' - title: Suspended At - internal: + title: Purpose + enabled: type: boolean - title: Internal - default: false - balances_micro: - additionalProperties: true - type: object - title: Balances Micro - counts: - additionalProperties: - type: integer - type: object - title: Counts - additionalProperties: true - type: object - required: - - id - - name - - balances_micro - - counts - title: AdminOrganization - AdminOrganizationDetail: - properties: - organization: + title: Enabled + version: + type: integer + title: Version + current_version_id: + anyOf: + - type: string + - type: 'null' + title: Current Version Id + rules: additionalProperties: true type: object - title: Organization - members: - items: - additionalProperties: true - type: object - type: array - title: Members - keys: - items: - additionalProperties: true - type: object - type: array - title: Keys - recent_jobs: - items: - additionalProperties: true - type: object - type: array - title: Recent Jobs - ledger: - additionalProperties: true - type: object - title: Ledger - open_reviews: - items: - additionalProperties: true - type: object - type: array - title: Open Reviews + title: Rules + managed: + anyOf: + - type: string + - type: 'null' + title: Managed + description: connected_apps when a Settings switch (the auto-send setting) manages this policy. Such a + policy is read here and changed only through PUT /v1/organizations/current/auto-send. + created_by: + type: string + title: Created By + created_at: + type: string + title: Created At + updated_at: + type: string + title: Updated At additionalProperties: true type: object required: - - organization - - members - - keys - - recent_jobs - - ledger - - open_reviews - title: AdminOrganizationDetail - AdminOrganizationPage: + - id + - name + - purpose + - enabled + - version + - current_version_id + - rules + - created_by + - created_at + - updated_at + title: Policy + PolicyCreate: properties: - items: - items: - $ref: '#/components/schemas/AdminOrganization' - type: array - title: Items - next_cursor: + name: + type: string + maxLength: 120 + minLength: 1 + title: Name + purpose: anyOf: - type: string + maxLength: 500 - type: 'null' - title: Next Cursor - additionalProperties: true + title: Purpose + description: Why this policy exists. Shown, never evaluated. + rules: + additionalProperties: true + type: object + title: Rules + description: See the PolicyRules schema in docs/APPROVALS.md. + enabled: + type: boolean + title: Enabled + default: true + additionalProperties: false type: object required: - - items - title: AdminOrganizationPage - AdminRevenue: + - name + - rules + title: PolicyCreate + PolicyEvaluation: properties: - computed_at: + decision: type: string - title: Computed At - days: - type: integer - title: Days - captured: - additionalProperties: true - type: object - title: Captured - funded: - additionalProperties: true - type: object - title: Funded - refunded: - additionalProperties: true - type: object - title: Refunded - recurring: - additionalProperties: true - type: object - title: Recurring - balances_micro: - additionalProperties: - additionalProperties: - type: integer - type: object - type: object - title: Balances Micro - by_day: - items: - additionalProperties: true - type: object - type: array - title: By Day - data_age: - additionalProperties: true - type: object - title: Data Age - sources: + title: Decision + rule: + anyOf: + - type: string + - type: 'null' + title: Rule + reason: + type: string + title: Reason + policy_id: + anyOf: + - type: string + - type: 'null' + title: Policy Id + policy_version_id: + anyOf: + - type: string + - type: 'null' + title: Policy Version Id + policy_name: + anyOf: + - type: string + - type: 'null' + title: Policy Name + version: + anyOf: + - type: integer + - type: 'null' + title: Version + denials: items: additionalProperties: true type: object type: array - title: Sources - additionalProperties: true - type: object - required: - - computed_at - - days - - captured - - funded - - refunded - - recurring - - balances_micro - - by_day - - data_age - - sources - title: AdminRevenue - AdminStats: - properties: - computed_at: - type: string - title: Computed At - jobs_by_state: - additionalProperties: - type: integer - type: object - title: Jobs By State - by_environment: - additionalProperties: true - type: object - title: By Environment - live_sends: - additionalProperties: true - type: object - title: Live Sends - needs_you: - additionalProperties: true - type: object - title: Needs You - window: - additionalProperties: true - type: object - title: Window - totals: + title: Denials + plan: additionalProperties: true type: object - title: Totals - money_live_micro: + title: Plan + executed: + type: boolean + title: Executed + default: false + usage: + type: + - object + - 'null' additionalProperties: type: integer - type: object - title: Money Live Micro + description: Returned by POST /v1/policy-evaluations when a bound policy has usage. + x-sdk-route: POST /v1/policy-evaluations additionalProperties: true type: object required: - - computed_at - - jobs_by_state - - by_environment - - live_sends - - needs_you - - window - - totals - - money_live_micro - title: AdminStats - AdminWhoami: + - decision + - rule + - reason + - policy_id + - policy_version_id + - policy_name + - version + - denials + - plan + title: PolicyEvaluation + PolicyEvaluationCreate: properties: - kind: - type: string - enum: - - user - - service - - dev - title: Kind - identity: - type: string - title: Identity - actor: - type: string - title: Actor - read_only: - type: boolean - title: Read Only - additionalProperties: true + quote_id: + anyOf: + - type: string + pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ + - type: 'null' + title: Quote Id + description: Evaluate an existing quote. + document_id: + anyOf: + - type: string + pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ + - type: 'null' + title: Document Id + description: 'Or plan: this document ...' + to: + anyOf: + - type: string + maxLength: 32 + - type: 'null' + title: To + description: '... to this E.164 destination.' + from: + anyOf: + - type: string + maxLength: 32 + - type: 'null' + title: From + additionalProperties: false type: object - required: - - kind - - identity - - actor - - read_only - title: AdminWhoami - AnalyticsJourneys: + title: PolicyEvaluationCreate + PolicyList: properties: - computed_at: - type: string - title: Computed At - days: - type: integer - title: Days - window: - additionalProperties: - type: string - type: object - title: Window - cohort: - type: integer - title: Cohort - funnel: + items: items: - additionalProperties: true - type: object + $ref: '#/components/schemas/Policy' type: array - title: Funnel - leakiest: + title: Items + additionalProperties: true + type: object + required: + - items + title: PolicyList + PolicyUpdate: + properties: + name: + anyOf: + - type: string + maxLength: 120 + minLength: 1 + - type: 'null' + title: Name + purpose: + anyOf: + - type: string + maxLength: 500 + - type: 'null' + title: Purpose + rules: anyOf: - additionalProperties: true type: object - type: 'null' - title: Leakiest - doors: - items: - additionalProperties: true - type: object - type: array - title: Doors - topups: - additionalProperties: true - type: object - title: Topups - site: + title: Rules + enabled: + anyOf: + - type: boolean + - type: 'null' + title: Enabled + additionalProperties: false + type: object + title: PolicyUpdate + PolicyVersion: + properties: + id: + type: string + title: Id + policy_id: + type: string + title: Policy Id + version: + type: integer + title: Version + name: + type: string + title: Name + purpose: + anyOf: + - type: string + - type: 'null' + title: Purpose + enabled: + type: boolean + title: Enabled + rules: additionalProperties: true type: object - title: Site - organizations: + title: Rules + created_by: + type: string + title: Created By + created_at: + type: string + title: Created At + additionalProperties: true + type: object + required: + - id + - policy_id + - version + - name + - purpose + - enabled + - rules + - created_by + - created_at + title: PolicyVersion + PolicyVersionList: + properties: + items: items: - additionalProperties: true - type: object + $ref: '#/components/schemas/PolicyVersion' type: array - title: Organizations - listed: - type: integer - title: Listed + title: Items additionalProperties: true type: object required: - - computed_at - - days - - window - - cohort - - funnel - - doors - - topups - - site - - organizations - - listed - title: AnalyticsJourneys - AnalyticsOrganization: + - items + title: PolicyVersionList + PortOrder: properties: - computed_at: + id: type: string - title: Computed At - days: - type: integer - title: Days - window: - additionalProperties: + title: Id + environment: + type: string + enum: + - live + - test + title: Environment + direction: + type: string + title: Direction + status: + type: string + title: Status + numbers: + items: type: string - type: object - title: Window - organization: + type: array + title: Numbers + request: additionalProperties: true type: object - title: Organization - steps: - items: - additionalProperties: true - type: object - type: array - title: Steps - members: + title: Request + milestones: items: additionalProperties: true type: object type: array - title: Members - doors: + title: Milestones + provider: + type: string + title: Provider + created_at: + type: string + title: Created At + updated_at: + type: string + title: Updated At + additionalProperties: true + type: object + required: + - id + - environment + - direction + - status + - numbers + - request + - milestones + - provider + - created_at + - updated_at + title: PortOrder + PortOrderCreate: + properties: + numbers: items: - additionalProperties: true - type: object + type: string type: array - title: Doors - keys: - additionalProperties: true - type: object - title: Keys - jobs: - additionalProperties: true - type: object - title: Jobs - topups: + maxItems: 50 + minItems: 1 + title: Numbers + note: + anyOf: + - type: string + maxLength: 500 + - type: 'null' + title: Note + additionalProperties: false + type: object + required: + - numbers + title: PortOrderCreate + PortOrderList: + properties: + items: + items: + $ref: '#/components/schemas/PortOrder' + type: array + title: Items + additionalProperties: true + type: object + required: + - items + title: PortOrderList + PortOutRequestCreate: + properties: + number_id: + type: string + title: Number Id + note: + anyOf: + - type: string + maxLength: 500 + - type: 'null' + title: Note + additionalProperties: false + type: object + required: + - number_id + title: PortOutRequestCreate + PortabilityCheck: + properties: + items: items: additionalProperties: true type: object type: array - title: Topups - inbound: - additionalProperties: true - type: object - title: Inbound + title: Items + checked_at: + type: string + title: Checked At + additionalProperties: true + type: object + required: + - items + - checked_at + title: PortabilityCheck + PortabilityCheckCreate: + properties: numbers: - additionalProperties: true - type: object + items: + type: string + type: array + maxItems: 50 + minItems: 1 title: Numbers - actions: + additionalProperties: false + type: object + required: + - numbers + title: PortabilityCheckCreate + PortalSession: + properties: + url: + type: string + title: Url + expires_at: + type: string + title: Expires At + additionalProperties: true + type: object + required: + - url + - expires_at + title: PortalSession + ProviderHealth: + properties: + name: + type: string + title: Name + configured: + type: boolean + title: Configured + serves: items: - additionalProperties: true - type: object + type: string type: array - title: Actions - recent: + title: Serves + default: [] + circuit: + type: string + title: Circuit + routes_paused: items: - additionalProperties: true - type: object + type: string type: array - title: Recent - usage: + title: Routes Paused + breakers: items: additionalProperties: true type: object type: array - title: Usage - tools: + title: Breakers + default: [] + last_success_at: + anyOf: + - type: string + - type: 'null' + title: Last Success At + last_failure_at: + anyOf: + - type: string + - type: 'null' + title: Last Failure At + concurrency_in_use: + type: integer + title: Concurrency In Use + additionalProperties: true + type: object + required: + - name + - configured + - circuit + - routes_paused + - concurrency_in_use + title: ProviderHealth + ProviderList: + properties: + items: + items: + $ref: '#/components/schemas/ProviderHealth' + type: array + title: Items + additionalProperties: true + type: object + required: + - items + title: ProviderList + Providers: + properties: + magic_link: + type: boolean + title: Magic Link + default: true + passkeys: + type: boolean + title: Passkeys + default: true + github: + type: boolean + title: Github + google: + type: boolean + title: Google + additionalProperties: true + type: object + required: + - github + - google + title: Providers + Quote: + properties: + id: + type: string + title: Id + environment: + type: string + enum: + - live + - test + title: Environment + document_id: + type: string + title: Document Id + document_sha256: + type: string + title: Document Sha256 + to: + type: string + title: To + from: + anyOf: + - type: string + - type: 'null' + title: From + route: + type: string + title: Route + document_pages: + type: integer + title: Document Pages + cover_pages: + type: integer + title: Cover Pages + billable_pages: + type: integer + title: Billable Pages + amount_micro: + type: integer + title: Amount Micro + currency: + type: string + title: Currency + price_version: + type: string + title: Price Version + per_page_micro: + type: integer + title: Per Page Micro + default: 0 + route_label: + type: string + title: Route Label + default: '' + cover: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Cover + quality: + type: string + title: Quality + default: fine + sender_name: + anyOf: + - type: string + - type: 'null' + title: Sender Name + delete_after_send: + type: boolean + title: Delete After Send + default: false + authorization_required: + type: boolean + title: Authorization Required + warnings: items: additionalProperties: true type: object type: array - title: Tools + title: Warnings + expires_at: + type: string + title: Expires At + expired: + type: boolean + title: Expired + consumed_by_job_id: + anyOf: + - type: string + - type: 'null' + title: Consumed By Job Id + created_at: + type: string + title: Created At + pricing: + type: string + description: Returned by POST /v1/fax-quotes. Declared here until the public schema includes it. + x-sdk-route: POST /v1/fax-quotes + policy_evaluation: + type: + - object + - 'null' + description: Returned by GET /v1/fax-quotes/{quote_id}. Declared here until the public schema includes + it. + x-sdk-route: GET /v1/fax-quotes/{quote_id} + additionalProperties: true additionalProperties: true type: object required: - - computed_at - - days - - window - - organization - - steps - - members - - doors - - keys - - jobs - - topups - - inbound - - numbers - - actions - - recent - - usage - - tools - title: AnalyticsOrganization - AnalyticsSource: + - id + - environment + - document_id + - document_sha256 + - to + - route + - document_pages + - cover_pages + - billable_pages + - amount_micro + - currency + - price_version + - cover + - authorization_required + - warnings + - expires_at + - expired + - consumed_by_job_id + - created_at + title: Quote + QuoteCreate: + properties: + document_id: + type: string + title: Document Id + to: + type: string + title: To + description: Destination in E.164, for example +12025550123. + from: + anyOf: + - type: string + - type: 'null' + title: From + description: A number you own; omit for the shared pool. + cover: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Cover + description: 'Cover sheet fields: to_name, from_name, subject, message. Counts as a page.' + quality: + type: string + enum: + - standard + - fine + - superfine + - ultra + title: Quality + description: 'Fax resolution. standard is the fax standard''s 204 by 98 dpi, fine (the default) 204 by + 196, superfine 204 by 392. ultra asks the carrier for its darkest text processing at the same superfine + resolution and takes longer to process: measured 2026-11-09, a page sent at ultra and one at superfine + both arrived as 1728 by 4112 pixels, 204 by 391 dpi. The price does not change. The job echoes the level.' + default: fine + sender_name: + anyOf: + - type: string + maxLength: 128 + - type: 'null' + title: Sender Name + description: The station name the carrier prints in the fax header, next to the sending number. Letters, + digits, spaces and - _ ~ ! . + only, at most 128 characters. Omit to show the number alone. + delete_after_send: + type: boolean + title: Delete After Send + description: 'Delete the document and the transmitted copy the moment the fax ends in delivered, partial + or failed. The job, its receipt and the carrier evidence stay; they hold no document content. A fax + that ends unknown keeps the document until a reconcile settles it. Default: keep the document for the + retention period.' + default: false + additionalProperties: false + type: object + required: + - document_id + - to + title: QuoteCreate + Reauth: + properties: + method: + type: string + enum: + - passkey + - magic_link + title: Method + credential: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Credential + additionalProperties: false + type: object + required: + - method + title: Reauth + Receipt: + properties: + fax_job_id: + type: string + title: Fax Job Id + state: + type: string + title: State + failure_code: + anyOf: + - type: string + - type: 'null' + title: Failure Code + to: + type: string + title: To + from: + anyOf: + - type: string + - type: 'null' + title: From + document_sha256: + type: string + title: Document Sha256 + transmitted_sha256: + anyOf: + - type: string + - type: 'null' + title: Transmitted Sha256 + billable_pages: + type: integer + title: Billable Pages + cover_pages: + type: integer + title: Cover Pages + description: 'How many of the billable pages are the cover sheet: 0 or 1.' + default: 0 + pages_sent: + type: integer + title: Pages Sent + amount_micro: + type: integer + title: Amount Micro + captured_micro: + type: integer + title: Captured Micro + currency: + type: string + title: Currency + provider: + type: string + title: Provider + created_at: + type: string + title: Created At + completed_at: + anyOf: + - type: string + - type: 'null' + title: Completed At + evidence: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + title: Evidence + attempts: + items: + $ref: '#/components/schemas/Attempt' + type: array + title: Attempts + note: + type: string + title: Note + generated_at: + type: string + title: Generated At + one_time: + anyOf: + - $ref: '#/components/schemas/OneTimeJob' + - type: 'null' + description: Set on the receipt of a fax that a one-time order paid for. It names the order, its status, + the card and the amount the card was authorized for. captured_micro in it is the amount charged, written + once the payment provider confirmed the capture. For a card order read the money here. Null for a fax + paid from the wallet. + additionalProperties: true + type: object + required: + - fax_job_id + - state + - failure_code + - to + - document_sha256 + - transmitted_sha256 + - billable_pages + - pages_sent + - amount_micro + - captured_micro + - currency + - provider + - created_at + - completed_at + - evidence + - attempts + - note + - generated_at + title: Receipt + ReconcileResult: properties: - source: - type: string - title: Source - label: - type: string - title: Label - status: + outcome: type: string - title: Status - missing: - items: - type: string - type: array - title: Missing - step: - type: integer - title: Step - error: + enum: + - no_provider_ref + - skipped + - poll_failed + - finalized + - retry_scheduled + - unknown + - pending + title: Outcome + job: + $ref: '#/components/schemas/AdminJob' + additionalProperties: true + type: object + required: + - outcome + - job + title: ReconcileResult + ReconciliationReport: + properties: + report: anyOf: - - type: string + - additionalProperties: true + type: object - type: 'null' - title: Error - last_pulled_at: + title: Report + last_day: anyOf: - type: string - type: 'null' - title: Last Pulled At - latest_day: + title: Last Day + additionalProperties: true + type: object + required: + - report + - last_day + title: ReconciliationReport + ReleaseRequest: + properties: + confirm: anyOf: - type: string - type: 'null' - title: Latest Day - additionalProperties: true + title: Confirm + description: The number itself, typed out, to confirm. + additionalProperties: false type: object - required: - - source - - label - - status - - missing - - step - title: AnalyticsSource - AnalyticsSources: + title: ReleaseRequest + ReleaseResult: properties: - items: - items: - $ref: '#/components/schemas/AnalyticsSource' - type: array - title: Items - pull: + settlement: additionalProperties: true type: object - title: Pull + title: Settlement + job: + $ref: '#/components/schemas/AdminJob' additionalProperties: true type: object required: - - items - - pull - title: AnalyticsSources - AnalyticsUsage: + - settlement + - job + title: ReleaseResult + Requested: properties: - computed_at: + requested: + type: boolean + title: Requested + kind: type: string - title: Computed At - days: + enum: + - export + - deletion + title: Kind + message: + type: string + title: Message + additionalProperties: true + type: object + required: + - requested + - kind + - message + title: Requested + ReviewAccessAction: + properties: + reason: + type: string + maxLength: 500 + minLength: 3 + title: Reason + description: 'Why: recorded on the audit row' + additionalProperties: false + type: object + required: + - reason + title: ReviewAccessAction + ReviewAccessCreate: + properties: + reason: + type: string + maxLength: 500 + minLength: 3 + title: Reason + description: 'Why: recorded on the audit row' + email: + type: string + format: email + title: Email + label: + type: string + maxLength: 120 + minLength: 1 + title: Label + default: Directory review + ttl_days: type: integer - title: Days - window: - additionalProperties: - type: string - type: object - title: Window - org_id: - anyOf: - - type: string - - type: 'null' - title: Org Id - totals: - additionalProperties: true - type: object - title: Totals - by_day: - items: - additionalProperties: true - type: object - type: array - title: By Day - by_surface: - items: - additionalProperties: true - type: object - type: array - title: By Surface - by_family: - items: - additionalProperties: true - type: object - type: array - title: By Family - top_organizations: - items: - additionalProperties: true - type: object - type: array - title: Top Organizations - top_keys: - items: - additionalProperties: true - type: object - type: array - title: Top Keys - tools: - additionalProperties: true - type: object - title: Tools - data_age: - additionalProperties: true - type: object - title: Data Age + maximum: 90.0 + minimum: 1.0 + title: Ttl Days + default: 30 + additionalProperties: false + type: object + required: + - reason + - email + title: ReviewAccessCreate + ReviewAccessIssued: + properties: + id: + type: string + title: Id + email: + type: string + title: Email + label: + type: string + title: Label + code: + type: string + title: Code + expires_at: + type: string + title: Expires At additionalProperties: true type: object required: - - computed_at - - days - - window - - totals - - by_day - - by_surface - - by_family - - top_organizations - - top_keys - - tools - - data_age - title: AnalyticsUsage - AnalyticsWebsite: + - id + - email + - label + - code + - expires_at + title: ReviewAccessIssued + ReviewSignIn: + properties: + email: + type: string + format: email + title: Email + code: + type: string + maxLength: 256 + minLength: 1 + format: password + title: Code + writeOnly: true + additionalProperties: false + type: object + required: + - email + - code + title: ReviewSignIn + Revoked: properties: - computed_at: - type: string - title: Computed At - days: + revoked: type: integer - title: Days - window: - additionalProperties: - type: string - type: object - title: Window - sources: + title: Revoked + additionalProperties: true + type: object + required: + - revoked + title: Revoked + RouteList: + properties: + items: items: - $ref: '#/components/schemas/AnalyticsSource' + $ref: '#/components/schemas/RouteState' type: array - title: Sources - search: - additionalProperties: true - type: object - title: Search - traffic: - additionalProperties: true - type: object - title: Traffic - site: - additionalProperties: true - type: object - title: Site - ux: - additionalProperties: true - type: object - title: Ux - bing: - additionalProperties: true - type: object - title: Bing - authority: - additionalProperties: true - type: object - title: Authority - data_age: - additionalProperties: true - type: object - title: Data Age + title: Items additionalProperties: true type: object required: - - computed_at - - days - - window - - sources - - search - - traffic - - site - - ux - - bing - - authority - - data_age - title: AnalyticsWebsite - AuditEvent: + - items + title: RouteList + RouteState: properties: - id: + route: type: string - title: Id - org_id: + title: Route + status: + type: string + enum: + - paused + - open + - breaker_open + title: Status + reason: anyOf: - type: string - type: 'null' - title: Org Id + title: Reason actor: type: string title: Actor - action: + updated_at: type: string - title: Action - resource_type: + title: Updated At + paused_at: + anyOf: + - type: string + - type: 'null' + title: Paused At + open_until: + anyOf: + - type: string + - type: 'null' + title: Open Until + additionalProperties: true + type: object + required: + - route + - status + - actor + - updated_at + title: RouteState + SandboxFixture: + properties: + id: type: string - title: Resource Type - resource_id: + title: Id + scenario: + type: string + title: Scenario + destination: anyOf: - type: string - type: 'null' - title: Resource Id + title: Destination created_at: type: string title: Created At + expires_at: + type: string + title: Expires At additionalProperties: true type: object required: - id - - actor - - action - - resource_type + - scenario + - destination - created_at - title: AuditEvent - AuditEventPage: + - expires_at + title: SandboxFixture + SandboxInboundCreate: properties: - items: - items: - $ref: '#/components/schemas/AuditEvent' - type: array - title: Items - next_cursor: + to: + type: string + title: To + from: anyOf: - type: string - type: 'null' - title: Next Cursor + title: From + pages: + type: integer + maximum: 200.0 + minimum: 1.0 + title: Pages + default: 1 + partial_content: + type: boolean + title: Partial Content + description: Rehearse a cut-off transmission. + default: false + additionalProperties: false + type: object + required: + - to + title: SandboxInboundCreate + SandboxReset: + properties: + reset: + type: boolean + title: Reset + deleted: + additionalProperties: + type: integer + type: object + title: Deleted + wallet_available_micro: + type: integer + title: Wallet Available Micro additionalProperties: true type: object required: - - items - title: AuditEventPage - MagicLinkRequest: + - reset + - deleted + - wallet_available_micro + title: SandboxReset + SandboxScenarioCreate: properties: - email: + scenario: type: string - format: email - title: Email - next: + title: Scenario + destination: anyOf: - type: string - maxLength: 400 - type: 'null' - title: Next - description: An in-app path to open after sign-in. + title: Destination + description: Apply to one destination; omit for every send. additionalProperties: false type: object required: - - email - title: MagicLinkRequest - OAuthConsent: + - scenario + title: SandboxScenarioCreate + SandboxScenarios: properties: - id: - type: string - title: Id - client: - $ref: '#/components/schemas/OAuthConsentClient' - redirect_host: - type: string - title: Redirect Host - scopes_requested: + scenarios: items: - type: string + additionalProperties: + type: string + type: object type: array - title: Scopes Requested - organizations: + title: Scenarios + destinations: items: - $ref: '#/components/schemas/OAuthConsentOrganization' + additionalProperties: + type: string + type: object type: array - title: Organizations - default_environment: - type: string - enum: - - live - - test - title: Default Environment - expires_at: - type: string - title: Expires At - additionalProperties: true - type: object - required: - - id - - client - - redirect_host - - scopes_requested - - organizations - - default_environment - - expires_at - title: OAuthConsent - OAuthConsentClient: + title: Destinations + fixtures: + items: + $ref: '#/components/schemas/SandboxFixture' + type: array + title: Fixtures + additionalProperties: true + type: object + required: + - scenarios + - destinations + - fixtures + title: SandboxScenarios + SchemaCreate: properties: name: + type: string + maxLength: 120 + minLength: 1 + title: Name + description: anyOf: - type: string + maxLength: 500 - type: 'null' - title: Name - description: The client's own name, shown as text beside the host. - host: - type: string - title: Host - description: 'The host the person is sending a credential to: the trust anchor.' - loopback: - type: boolean - title: Loopback - description: The redirect goes to the person's own computer (an app they started). - first_seen: - type: boolean - title: First Seen - description: This client was registered during this request. - additionalProperties: true + title: Description + schema: + additionalProperties: true + type: object + title: Schema + additionalProperties: false type: object required: - name - - host - - loopback - - first_seen - title: OAuthConsentClient - OAuthConsentDecision: + - schema + title: SchemaCreate + SenderVerification: properties: - org_id: + id: type: string - maxLength: 64 - minLength: 1 - title: Org Id + title: Id environment: type: string enum: - live - test title: Environment - default: test - additionalProperties: false - type: object - required: - - org_id - title: OAuthConsentDecision - OAuthConsentOrganization: - properties: - id: + from: type: string - title: Id - name: + title: From + status: type: string - title: Name - role: + title: Status + method: type: string - title: Role - scopes: - items: - type: string - type: array - title: Scopes - description: What the grant would carry for this organization and role. + title: Method + number_id: + anyOf: + - type: string + - type: 'null' + title: Number Id + created_at: + type: string + title: Created At + verified_at: + anyOf: + - type: string + - type: 'null' + title: Verified At additionalProperties: true type: object required: - id - - name - - role - - scopes - title: OAuthConsentOrganization - OAuthRedirect: + - environment + - from + - status + - method + - number_id + - created_at + - verified_at + title: SenderVerification + SenderVerificationCreate: properties: - redirect_to: + from: type: string - title: Redirect To - description: 'Where the browser goes next: the client''s registered redirect URI.' - additionalProperties: true + title: From + description: A number your organization owns, E.164. + additionalProperties: false type: object required: - - redirect_to - title: OAuthRedirect - OneTimeConfig: + - from + title: SenderVerificationCreate + Sent: properties: - enabled: + sent: type: boolean - title: Enabled - reason: - anyOf: - - type: string - - type: 'null' - title: Reason - price_version: - type: string - title: Price Version - effective_from: - type: string - title: Effective From - currency: - type: string - title: Currency - routes: - additionalProperties: - $ref: '#/components/schemas/OneTimeRoute' - type: object - title: Routes - max_document_pages: - type: integer - title: Max Document Pages - max_upload_bytes: - type: integer - title: Max Upload Bytes - max_text_chars: - type: integer - title: Max Text Chars - formats: - items: - type: string - type: array - title: Formats - aup_version: - type: string - title: Aup Version - aup_url: - type: string - title: Aup Url - terms_url: + title: Sent + message: type: string - title: Terms Url + title: Message additionalProperties: true type: object required: - - enabled - - price_version - - effective_from - - currency - - routes - - max_document_pages - - max_upload_bytes - - max_text_chars - - formats - - aup_version - - aup_url - - terms_url - title: OneTimeConfig - description: |- - What the public send page and an agent read before an order: whether the product is open here, the - card the order will be priced from (present only while open), the document limits and the policy the - sender accepts. Prices are integer micro-USD; the dollar figure is the client's to format. - OneTimeRoute: + - sent + - message + title: Sent + ServiceStatus: properties: - label: + status: type: string - title: Label - flat_micro: - type: integer - title: Flat Micro - included_pages: - type: integer - title: Included Pages - extra_page_micro: - type: integer - title: Extra Page Micro - cover_included: + enum: + - ok + - degraded + title: Status + version: + type: string + title: Version + environment: + type: string + enum: + - live + - test + title: Environment + provider: + type: string + title: Provider + database: type: boolean - title: Cover Included + title: Database + worker_beat_age_s: + anyOf: + - type: integer + - type: 'null' + title: Worker Beat Age S + status_page: + anyOf: + - type: string + - type: 'null' + title: Status Page additionalProperties: true type: object required: - - label - - flat_micro - - included_pages - - extra_page_micro - - cover_included - title: OneTimeRoute - OutboxDiscarded: + - status + - version + - environment + - provider + - database + title: ServiceStatus + SessionInfo: properties: id: type: string title: Id - discarded: + auth_method: + type: string + title: Auth Method + authenticated_at: + type: string + title: Authenticated At + fresh_until: + type: string + title: Fresh Until + fresh: type: boolean - title: Discarded + title: Fresh + expires_at: + type: string + title: Expires At additionalProperties: true type: object required: - id - - discarded - title: OutboxDiscarded - OutboxPage: + - auth_method + - authenticated_at + - fresh_until + - fresh + - expires_at + title: SessionInfo + SessionList: properties: items: items: - $ref: '#/components/schemas/OutboxRow' + $ref: '#/components/schemas/SessionOut' type: array title: Items - next_cursor: - anyOf: - - type: string - - type: 'null' - title: Next Cursor additionalProperties: true type: object required: - items - title: OutboxPage - OutboxRow: + title: SessionList + SessionOut: properties: id: type: string title: Id - kind: - type: string - title: Kind - status: + current: + type: boolean + title: Current + auth_method: type: string - enum: - - queued - - running - - done - - failed - - dead - title: Status - title: - anyOf: - - type: string - - type: 'null' - title: Title - error: + title: Auth Method + ip: anyOf: - type: string - type: 'null' - title: Error - attempts: - type: integer - title: Attempts - max_attempts: - type: integer - title: Max Attempts - org_id: + title: Ip + user_agent: anyOf: - type: string - type: 'null' - title: Org Id + title: User Agent created_at: type: string title: Created At - finished_at: - anyOf: - - type: string - - type: 'null' - title: Finished At - age_s: - type: integer - title: Age S + authenticated_at: + type: string + title: Authenticated At + last_seen_at: + type: string + title: Last Seen At + expires_at: + type: string + title: Expires At additionalProperties: true type: object required: - id - - kind - - status - - attempts - - max_attempts + - current + - auth_method + - ip + - user_agent - created_at - - age_s - title: OutboxRow - Passkey: + - authenticated_at + - last_seen_at + - expires_at + title: SessionOut + SignedUrl: + properties: + url: + type: string + title: Url + expires_at: + type: string + title: Expires At + content_type: + type: string + title: Content Type + additionalProperties: true + type: object + required: + - url + - expires_at + - content_type + title: SignedUrl + Subscription: properties: id: type: string title: Id - label: + product: + type: string + title: Product + status: + type: string + title: Status + stripe_subscription_id: + type: string + title: Stripe Subscription Id + stripe_price_id: anyOf: - type: string - type: 'null' - title: Label - aaguid: + title: Stripe Price Id + cancel_at_period_end: + type: boolean + title: Cancel At Period End + current_period_end: anyOf: - type: string - type: 'null' - title: Aaguid - transports: - anyOf: - - items: - type: string - type: array - - type: 'null' - title: Transports - backup_eligible: + title: Current Period End + entitled: type: boolean - title: Backup Eligible + title: Entitled created_at: type: string title: Created At - last_used_at: + updated_at: + type: string + title: Updated At + canceled_at: anyOf: - type: string - type: 'null' - title: Last Used At + title: Canceled At additionalProperties: true type: object required: - id - - label - - aaguid - - transports - - backup_eligible + - product + - status + - stripe_subscription_id + - stripe_price_id + - cancel_at_period_end + - current_period_end + - entitled - created_at - - last_used_at - title: Passkey - PasskeyList: - properties: - items: - items: - $ref: '#/components/schemas/Passkey' - type: array - title: Items - additionalProperties: true - type: object - required: - - items - title: PasskeyList - PasskeyLoginVerify: - properties: - credential: - additionalProperties: true - type: object - title: Credential - additionalProperties: false - type: object - required: - - credential - title: PasskeyLoginVerify - PasskeyRegisterVerify: - properties: - credential: - additionalProperties: true - type: object - title: Credential - label: - anyOf: - - type: string - maxLength: 80 - - type: 'null' - title: Label - additionalProperties: false - type: object - required: - - credential - title: PasskeyRegisterVerify - ProviderHealth: + - updated_at + - canceled_at + title: Subscription + SubscriptionCheckout: properties: - name: + url: type: string - title: Name - configured: - type: boolean - title: Configured - serves: - items: - type: string - type: array - title: Serves - default: [] - circuit: + title: Url + stripe_session_id: type: string - title: Circuit - routes_paused: - items: - type: string - type: array - title: Routes Paused - breakers: - items: - additionalProperties: true - type: object - type: array - title: Breakers - default: [] - last_success_at: - anyOf: - - type: string - - type: 'null' - title: Last Success At - last_failure_at: - anyOf: - - type: string - - type: 'null' - title: Last Failure At - concurrency_in_use: - type: integer - title: Concurrency In Use + title: Stripe Session Id + product: + type: string + title: Product additionalProperties: true type: object required: - - name - - configured - - circuit - - routes_paused - - concurrency_in_use - title: ProviderHealth - ProviderList: + - url + - stripe_session_id + - product + title: SubscriptionCheckout + SubscriptionList: properties: items: items: - $ref: '#/components/schemas/ProviderHealth' + $ref: '#/components/schemas/Subscription' type: array title: Items + workspace: + type: boolean + title: Workspace + next_cursor: + anyOf: + - type: string + - type: 'null' + title: Next Cursor additionalProperties: true type: object required: - items - title: ProviderList - Providers: + - workspace + title: SubscriptionList + SummaryCreate: properties: - magic_link: - type: boolean - title: Magic Link - default: true - passkeys: - type: boolean - title: Passkeys - default: true - github: - type: boolean - title: Github - google: + inbox_item_id: + anyOf: + - type: string + pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ + - type: 'null' + title: Inbox Item Id + document_id: + anyOf: + - type: string + pattern: ^[a-z]{2,8}_[0-9a-f]{32}$ + - type: 'null' + title: Document Id + max_pages: + anyOf: + - type: integer + maximum: 200.0 + minimum: 1.0 + - type: 'null' + title: Max Pages + strict: type: boolean - title: Google - additionalProperties: true + title: Strict + description: Refuse with page_limit_exceeded when the document has more pages than would be read, instead + of reading the first ones and warning. + default: false + strong_model_if_below: + type: number + maximum: 1.0 + minimum: 0.0 + title: Strong Model If Below + default: 0.75 + additionalProperties: false type: object - required: - - github - - google - title: Providers - Reauth: + title: SummaryCreate + SwitchOrganization: properties: - method: + org_id: type: string - enum: - - passkey - - magic_link - title: Method - credential: - anyOf: - - additionalProperties: true - type: object - - type: 'null' - title: Credential + title: Org Id additionalProperties: false type: object required: - - method - title: Reauth - ReconcileResult: + - org_id + title: SwitchOrganization + TelnyxAck: properties: + received: + type: boolean + title: Received outcome: type: string - enum: - - no_provider_ref - - skipped - - poll_failed - - finalized - - retry_scheduled - - unknown - - pending title: Outcome - job: - $ref: '#/components/schemas/AdminJob' - additionalProperties: true - type: object - required: - - outcome - - job - title: ReconcileResult - ReconciliationReport: - properties: - report: - anyOf: - - additionalProperties: true - type: object - - type: 'null' - title: Report - last_day: + event_id: anyOf: - type: string - type: 'null' - title: Last Day - additionalProperties: true - type: object - required: - - report - - last_day - title: ReconciliationReport - ReleaseResult: - properties: - settlement: - additionalProperties: true - type: object - title: Settlement - job: - $ref: '#/components/schemas/AdminJob' + title: Event Id additionalProperties: true type: object required: - - settlement - - job - title: ReleaseResult - Revoked: + - received + - outcome + title: TelnyxAck + TopupCreate: properties: - revoked: + amount_usd: type: integer - title: Revoked - additionalProperties: true - type: object - required: - - revoked - title: Revoked - RouteList: - properties: - items: - items: - $ref: '#/components/schemas/RouteState' - type: array - title: Items - additionalProperties: true + maximum: 1000.0 + minimum: 10.0 + title: Amount Usd + description: Whole US dollars, 10 to 1000 per session. + additionalProperties: false type: object required: - - items - title: RouteList - RouteState: + - amount_usd + title: TopupCreate + TopupSession: properties: - route: + id: type: string - title: Route + title: Id + environment: + type: string + enum: + - live + - test + title: Environment + kind: + type: string + enum: + - manual + - auto + - setup + title: Kind + amount_micro: + type: integer + title: Amount Micro + currency: + type: string + title: Currency status: type: string enum: - - paused - open - - breaker_open + - completed + - expired + - canceled + - failed title: Status - reason: + url: anyOf: - type: string - type: 'null' - title: Reason - actor: - type: string - title: Actor - updated_at: + title: Url + description: The hosted Checkout page while the session is open. + stripe_session_id: + anyOf: + - type: string + - type: 'null' + title: Stripe Session Id + stripe_payment_intent_id: + anyOf: + - type: string + - type: 'null' + title: Stripe Payment Intent Id + auto_topup_id: + anyOf: + - type: string + - type: 'null' + title: Auto Topup Id + error: + anyOf: + - type: string + - type: 'null' + title: Error + created_at: type: string - title: Updated At - paused_at: + title: Created At + expires_at: + anyOf: + - type: string + - type: 'null' + title: Expires At + completed_at: + anyOf: + - type: string + - type: 'null' + title: Completed At + additionalProperties: true + type: object + required: + - id + - environment + - kind + - amount_micro + - currency + - status + - stripe_session_id + - stripe_payment_intent_id + - auto_topup_id + - error + - created_at + - expires_at + - completed_at + title: TopupSession + TopupSessionPage: + properties: + items: + items: + $ref: '#/components/schemas/TopupSession' + type: array + title: Items + next_cursor: anyOf: - type: string - type: 'null' - title: Paused At - open_until: + title: Next Cursor + additionalProperties: true + type: object + required: + - items + title: TopupSessionPage + UploadComplete: + properties: + sha256: anyOf: - type: string + maxLength: 64 + minLength: 64 - type: 'null' - title: Open Until - additionalProperties: true + title: Sha256 + description: 'Optional: fail if the bytes differ.' + additionalProperties: false type: object - required: - - route - - status - - actor - - updated_at - title: RouteState - Sent: + title: UploadComplete + UploadCreate: properties: - sent: - type: boolean - title: Sent - message: + filename: type: string - title: Message - additionalProperties: true + maxLength: 255 + minLength: 1 + title: Filename + content_type: + type: string + maxLength: 100 + title: Content Type + description: application/pdf, image/png, image/jpeg or image/tiff. An image becomes 8-bit gray letter + pages, since fax is monochrome. A frame wider than it is tall is rotated. A frame above 300 dpi letter + is downsampled. Every TIFF frame is a page. PNG and JPEG take the first frame. The bytes must be the + declared type. + default: application/pdf + size_bytes: + type: integer + minimum: 1.0 + title: Size Bytes + additionalProperties: false type: object required: - - sent - - message - title: Sent - SessionInfo: + - filename + - size_bytes + title: UploadCreate + UploadSession: properties: id: type: string title: Id - auth_method: + status: type: string - title: Auth Method - authenticated_at: + title: Status + filename: type: string - title: Authenticated At - fresh_until: + title: Filename + content_type: type: string - title: Fresh Until - fresh: - type: boolean - title: Fresh + title: Content Type + declared_size: + type: integer + title: Declared Size + received_size: + type: integer + title: Received Size + sha256: + anyOf: + - type: string + - type: 'null' + title: Sha256 + created_at: + type: string + title: Created At expires_at: type: string title: Expires At + completed_at: + anyOf: + - type: string + - type: 'null' + title: Completed At + upload_url: + anyOf: + - type: string + - type: 'null' + title: Upload Url + upload_method: + anyOf: + - type: string + - type: 'null' + title: Upload Method + upload_url_expires_at: + anyOf: + - type: string + - type: 'null' + title: Upload Url Expires At additionalProperties: true type: object required: - id - - auth_method - - authenticated_at - - fresh_until - - fresh + - status + - filename + - content_type + - declared_size + - received_size + - sha256 + - created_at - expires_at - title: SessionInfo - SessionList: - properties: - items: - items: - $ref: '#/components/schemas/SessionOut' - type: array - title: Items - additionalProperties: true - type: object - required: - - items - title: SessionList - SessionOut: + - completed_at + title: UploadSession + UsageExport: properties: id: type: string title: Id - current: - type: boolean - title: Current - auth_method: + environment: type: string - title: Auth Method - ip: - anyOf: - - type: string - - type: 'null' - title: Ip - user_agent: + enum: + - live + - test + title: Environment + status: + type: string + enum: + - ready + - expired + title: Status + params: + additionalProperties: true + type: object + title: Params + row_count: + type: integer + title: Row Count + content_type: + type: string + title: Content Type + url: anyOf: - type: string - type: 'null' - title: User Agent + title: Url created_at: type: string title: Created At - authenticated_at: - type: string - title: Authenticated At - last_seen_at: - type: string - title: Last Seen At expires_at: type: string title: Expires At @@ -15009,44 +16767,118 @@ components: type: object required: - id - - current - - auth_method - - ip - - user_agent + - environment + - status + - params + - row_count + - content_type + - url - created_at - - authenticated_at - - last_seen_at - expires_at - title: SessionOut - SwitchOrganization: + title: UsageExport + UsageExportCreate: properties: - org_id: - type: string - title: Org Id + from: + anyOf: + - type: string + - type: 'null' + title: From + description: ISO date; default 30 days ago. + to: + anyOf: + - type: string + - type: 'null' + title: To + description: ISO date; default today. additionalProperties: false type: object + title: UsageExportCreate + UsageItem: + properties: + day: + anyOf: + - type: string + - type: 'null' + title: Day + direction: + anyOf: + - type: string + - type: 'null' + title: Direction + route: + anyOf: + - type: string + - type: 'null' + title: Route + destination: + anyOf: + - type: string + - type: 'null' + title: Destination + project_id: + anyOf: + - type: string + - type: 'null' + title: Project Id + pages: + type: integer + title: Pages + jobs: + type: integer + title: Jobs + amount_micro: + type: integer + title: Amount Micro + additionalProperties: true + type: object required: - - org_id - title: SwitchOrganization - TelnyxAck: + - pages + - jobs + - amount_micro + title: UsageItem + UsageReport: properties: - received: - type: boolean - title: Received - outcome: + environment: type: string - title: Outcome - event_id: - anyOf: - - type: string - - type: 'null' - title: Event Id + enum: + - live + - test + title: Environment + from: + type: string + title: From + to: + type: string + title: To + group_by: + items: + type: string + type: array + title: Group By + currency: + type: string + title: Currency + items: + items: + $ref: '#/components/schemas/UsageItem' + type: array + title: Items + totals: + additionalProperties: + type: integer + type: object + title: Totals additionalProperties: true type: object required: - - received - - outcome - title: TelnyxAck + - environment + - from + - to + - group_by + - currency + - items + - totals + title: UsageReport UserOut: properties: id: @@ -15081,6 +16913,76 @@ components: - created_at - last_login_at title: UserOut + ValidationError: + properties: + loc: + items: + anyOf: + - type: string + - type: integer + type: array + title: Location + msg: + type: string + title: Message + type: + type: string + title: Error Type + input: + title: Input + ctx: + type: object + title: Context + type: object + required: + - loc + - msg + - type + title: ValidationError + Webhook: + properties: + id: + type: string + title: Id + url: + type: string + title: Url + description: + anyOf: + - type: string + - type: 'null' + title: Description + event_types: + items: + type: string + type: array + title: Event Types + status: + type: string + title: Status + previous_secret_expires_at: + anyOf: + - type: string + - type: 'null' + title: Previous Secret Expires At + created_at: + type: string + title: Created At + updated_at: + type: string + title: Updated At + additionalProperties: true + type: object + required: + - id + - url + - description + - event_types + - status + - previous_secret_expires_at + - created_at + - updated_at + title: Webhook WebhookAck: properties: received: @@ -15095,97 +16997,125 @@ components: required: - outcome title: WebhookAck - faxforagents__api__routes__inbox__InboxItem: + WebhookCreate: properties: - id: + url: type: string - title: Id - from: + maxLength: 2000 + title: Url + description: anyOf: - type: string + maxLength: 200 - type: 'null' - title: From - to: - type: string - title: To - pages: - type: integer - title: Pages - status: - type: string - title: Status - document_id: + title: Description + event_types: anyOf: - - type: string + - items: + type: string + type: array - type: 'null' - title: Document Id - received_at: - type: string - title: Received At - amount_micro: - type: integer - title: Amount Micro - default: 0 - note: + title: Event Types + description: 'Empty or omitted: every event type.' + additionalProperties: false + type: object + required: + - url + title: WebhookCreate + WebhookList: + properties: + items: + items: + $ref: '#/components/schemas/Webhook' + type: array + title: Items + additionalProperties: true + type: object + required: + - items + title: WebhookList + WebhookUpdate: + properties: + url: anyOf: - type: string + maxLength: 2000 - type: 'null' - title: Note - environment: - type: string - enum: - - live - - test - title: Environment - default: live - number_id: + title: Url + description: anyOf: - type: string + maxLength: 200 - type: 'null' - title: Number Id - project_id: + title: Description + event_types: anyOf: - - type: string + - items: + type: string + type: array - type: 'null' - title: Project Id - partial_content: - type: boolean - title: Partial Content - default: false - media_status: - type: string - title: Media Status - default: stored - duration_secs: + title: Event Types + status: anyOf: - - type: integer + - type: string + enum: + - active + - paused - type: 'null' - title: Duration Secs - provider: + title: Status + additionalProperties: false + type: object + title: WebhookUpdate + WebhookWithSecret: + properties: + id: type: string - title: Provider - default: '' - read_at: + title: Id + url: + type: string + title: Url + description: anyOf: - type: string - type: 'null' - title: Read At - expires_at: + title: Description + event_types: + items: + type: string + type: array + title: Event Types + status: + type: string + title: Status + previous_secret_expires_at: anyOf: - type: string - type: 'null' - title: Expires At + title: Previous Secret Expires At + created_at: + type: string + title: Created At + updated_at: + type: string + title: Updated At + secret: + type: string + title: Secret + description: whsec_... Shown once. Verify webhook-signature with it. additionalProperties: true type: object required: - id - - to - - pages + - url + - description + - event_types - status - - document_id - - received_at - title: InboxItem - faxforagents__api__schemas__InboxItem: + - previous_secret_expires_at + - created_at + - updated_at + - secret + title: WebhookWithSecret + faxforagents__api__routes__inbox__InboxItem: properties: id: type: string @@ -15221,411 +17151,176 @@ components: - type: string - type: 'null' title: Note - additionalProperties: true - type: object - required: - - id - - to - - pages - - status - - document_id - - received_at - title: InboxItem - faxforagents__api__schemas__Me: - properties: - org_id: - type: string - title: Org Id - project_id: - type: string - title: Project Id - key_id: - type: string - title: Key Id - key_prefix: - type: string - title: Key Prefix environment: type: string enum: - live - test title: Environment - scopes: - items: - type: string - type: array - title: Scopes - actor: - type: string - title: Actor - additionalProperties: true - type: object - required: - - org_id - - project_id - - key_id - - key_prefix - - environment - - scopes - - actor - title: Me - faxforagents__auth__schemas__Me: - properties: - user: - $ref: '#/components/schemas/UserOut' - organization: - anyOf: - - $ref: '#/components/schemas/OrganizationSummary' - - type: 'null' - organizations: - items: - $ref: '#/components/schemas/OrganizationSummary' - type: array - title: Organizations - session: - $ref: '#/components/schemas/SessionInfo' - csrf_token: - type: string - title: Csrf Token - passkeys: - type: integer - title: Passkeys - providers: - $ref: '#/components/schemas/Providers' - additionalProperties: true - type: object - required: - - user - - organization - - organizations - - session - - csrf_token - - passkeys - - providers - title: Me - Client: - properties: - id: - type: string - title: Id - client_id: - type: string - title: Client Id - kind: - type: string - enum: - - static - - dynamic - - cimd - title: Kind - client_name: - anyOf: - - type: string - - type: 'null' - title: Client Name - client_host: - type: string - title: Client Host - created_at: - type: string - title: Created At - last_used_at: - anyOf: - - type: string - - type: 'null' - title: Last Used At - disabled_at: - anyOf: - - type: string - - type: 'null' - title: Disabled At - disabled_reason: - anyOf: - - type: string - - type: 'null' - title: Disabled Reason - additionalProperties: true - type: object - required: - - id - - client_id - - kind - - client_name - - client_host - - created_at - - last_used_at - - disabled_at - - disabled_reason - title: Client - ClientList: - properties: - items: - items: - $ref: '#/components/schemas/Client' - type: array - title: Items - next_cursor: - anyOf: - - type: string - - type: 'null' - title: Next Cursor - total: - type: integer - minimum: 0.0 - title: Total - additionalProperties: true - type: object - required: - - items - - total - title: ClientList - Grant: - properties: - id: - type: string - title: Id - org_id: - type: string - title: Org Id - organization: - type: string - title: Organization - user_id: - type: string - title: User Id - client_id: - type: string - title: Client Id - client_host: - type: string - title: Client Host - client_name: - anyOf: - - type: string - - type: 'null' - title: Client Name - environment: - type: string - enum: - - test - - live - title: Environment - scopes: - items: - type: string - type: array - title: Scopes - created_at: - type: string - title: Created At - last_used_at: + default: live + number_id: anyOf: - type: string - type: 'null' - title: Last Used At - revoked_at: + title: Number Id + project_id: anyOf: - type: string - type: 'null' - title: Revoked At - additionalProperties: true - type: object - required: - - id - - org_id - - organization - - user_id - - client_id - - client_host - - client_name - - environment - - scopes - - created_at - - last_used_at - - revoked_at - title: Grant - GrantList: - properties: - items: - items: - $ref: '#/components/schemas/Grant' - type: array - title: Items - next_cursor: + title: Project Id + partial_content: + type: boolean + title: Partial Content + default: false + media_status: + type: string + title: Media Status + default: stored + duration_secs: + anyOf: + - type: integer + - type: 'null' + title: Duration Secs + provider: + type: string + title: Provider + default: '' + read_at: anyOf: - type: string - type: 'null' - title: Next Cursor + title: Read At + expires_at: + anyOf: + - type: string + - type: 'null' + title: Expires At additionalProperties: true type: object required: - - items - title: GrantList - ReviewAccessAction: - properties: - reason: - type: string - maxLength: 500 - minLength: 3 - title: Reason - description: 'Why: recorded on the audit row' - additionalProperties: false - type: object - required: - - reason - title: ReviewAccessAction - ReviewAccessCreate: - properties: - reason: - type: string - maxLength: 500 - minLength: 3 - title: Reason - description: 'Why: recorded on the audit row' - email: - type: string - format: email - title: Email - label: - type: string - maxLength: 120 - minLength: 1 - title: Label - default: Directory review - ttl_days: - type: integer - maximum: 90.0 - minimum: 1.0 - title: Ttl Days - default: 30 - additionalProperties: false - type: object - required: - - reason - - email - title: ReviewAccessCreate - ReviewAccessIssued: + - id + - to + - pages + - status + - document_id + - received_at + title: InboxItem + faxforagents__api__schemas__InboxItem: properties: id: type: string title: Id - email: - type: string - title: Email - label: + from: + anyOf: + - type: string + - type: 'null' + title: From + to: type: string - title: Label - code: + title: To + pages: + type: integer + title: Pages + status: type: string - title: Code - expires_at: + title: Status + document_id: + anyOf: + - type: string + - type: 'null' + title: Document Id + received_at: type: string - title: Expires At + title: Received At + amount_micro: + type: integer + title: Amount Micro + default: 0 + note: + anyOf: + - type: string + - type: 'null' + title: Note additionalProperties: true type: object required: - id - - email - - label - - code - - expires_at - title: ReviewAccessIssued - ReviewSignIn: + - to + - pages + - status + - document_id + - received_at + title: InboxItem + faxforagents__api__schemas__Me: properties: - email: + org_id: type: string - format: email - title: Email - code: + title: Org Id + project_id: type: string - maxLength: 256 - minLength: 1 - format: password - title: Code - writeOnly: true - additionalProperties: false - type: object - required: - - email - - code - title: ReviewSignIn - AutoSendSetting: - properties: + title: Project Id + key_id: + type: string + title: Key Id + key_prefix: + type: string + title: Key Prefix environment: type: string enum: - live - test title: Environment - enabled: - type: boolean - title: Enabled - max_amount_micro: - type: integer - title: Max Amount Micro - description: Amount per fax, at most, in micro-units of USD. - daily_max_jobs: - type: integer - title: Daily Max Jobs - description: Faxes per UTC day, at most, across every connected app. - policy_id: - anyOf: - - type: string - - type: 'null' - title: Policy Id - description: The managed policy; null before the first enable. - version: - anyOf: - - type: integer - - type: 'null' - title: Version + scopes: + items: + type: string + type: array + title: Scopes + actor: + type: string + title: Actor additionalProperties: true type: object required: + - org_id + - project_id + - key_id + - key_prefix - environment - - enabled - - max_amount_micro - - daily_max_jobs - - policy_id - - version - title: AutoSendSetting - description: |- - The organization's auto-send setting for one environment: Settings, Connected apps, "Let connected - apps send without asking". Stored as the managed policy ``policy_id`` (docs/APPROVALS.md). - AutoSendUpdate: - properties: - enabled: - type: boolean - title: Enabled - max_amount_micro: - type: integer - maximum: 1000000000000.0 - minimum: 0.0 - title: Max Amount Micro - description: Amount per fax, at most, in micro-units of USD (5000000 is five US dollars). - default: 5000000 - daily_max_jobs: - type: integer - maximum: 100000.0 - minimum: 1.0 - title: Daily Max Jobs - description: Faxes per UTC day. - default: 20 - additionalProperties: false - type: object - required: - - enabled - title: AutoSendUpdate - NumberOrderList: + - scopes + - actor + title: Me + faxforagents__auth__schemas__Me: properties: - items: + user: + $ref: '#/components/schemas/UserOut' + organization: + anyOf: + - $ref: '#/components/schemas/OrganizationSummary' + - type: 'null' + organizations: items: - $ref: '#/components/schemas/NumberOrder' + $ref: '#/components/schemas/OrganizationSummary' type: array - title: Items + title: Organizations + session: + $ref: '#/components/schemas/SessionInfo' + csrf_token: + type: string + title: Csrf Token + passkeys: + type: integer + title: Passkeys + providers: + $ref: '#/components/schemas/Providers' additionalProperties: true type: object required: - - items - title: NumberOrderList + - user + - organization + - organizations + - session + - csrf_token + - passkeys + - providers + title: Me diff --git a/src/faxforagents/__init__.py b/src/faxforagents/__init__.py index 6eeebfe..1ce1233 100644 --- a/src/faxforagents/__init__.py +++ b/src/faxforagents/__init__.py @@ -18,8 +18,9 @@ from __future__ import annotations -__version__ = "0.3.2" +__version__ = "0.4.0" +from faxforagents._http import ORDER_TOKEN_HEADER from faxforagents.async_client import AsyncClient from faxforagents.client import TERMINAL_STATES, Client from faxforagents.errors import ( @@ -49,6 +50,7 @@ "FaxForAgentsError", "InvalidRequestError", "NotFoundError", + "ORDER_TOKEN_HEADER", "PaymentRequiredError", "RateLimitedError", "ScopeError", diff --git a/src/faxforagents/_http.py b/src/faxforagents/_http.py index 172e7b6..fb8680d 100644 --- a/src/faxforagents/_http.py +++ b/src/faxforagents/_http.py @@ -26,6 +26,8 @@ from faxforagents.errors import APIConnectionError, APIError, APITimeoutError, error_for DEFAULT_BASE_URL = "https://api.faxforagents.com" +#: The header that carries the status token of a one-time order, for a caller without a key. +ORDER_TOKEN_HEADER = "X-FFA-Order-Token" ENV_KEY = "FAXFORAGENTS_API_KEY" ENV_KEY_FALLBACK = "FFA_API_KEY" ENV_URL = "FAXFORAGENTS_API_URL" @@ -87,8 +89,10 @@ def __init__( max_retries: int, backoff_base: float, max_retry_after: float, + keyless: bool = False, ) -> None: - self.api_key = resolve_key(api_key) + # A keyless client sends no Authorization header. Only the one-time send answers it. + self.api_key: str | None = None if keyless else resolve_key(api_key) self.base_url = resolve_url(base_url) self.timeout = timeout self.max_retries = max(0, max_retries) @@ -96,11 +100,13 @@ def __init__( self.max_retry_after = max_retry_after def headers(self) -> dict[str, str]: - return { - "Authorization": f"Bearer {self.api_key}", + headers = { "Accept": "application/json", "User-Agent": f"faxforagents-python/{__version__}", } + if self.api_key is not None: + headers["Authorization"] = f"Bearer {self.api_key}" + return headers def wait_for(self, attempt: int, retry_after: str | None) -> float: explicit = retry_after_seconds(retry_after, self.max_retry_after) @@ -120,6 +126,8 @@ def __init__( params: Mapping[str, Any] | None = None, json: Any = None, content: bytes | None = None, + data: Mapping[str, str] | None = None, + files: Mapping[str, tuple[str, bytes, str]] | None = None, headers: Mapping[str, str] | None = None, idempotency_key: str | None = None, ) -> None: @@ -128,7 +136,10 @@ def __init__( self.params = clean_params(params) self.json = dump(json) if json is not None else None self.content = content - self.headers = dict(headers or {}) + # A multipart form: text fields in ``data``, ``(filename, bytes, content_type)`` parts in ``files``. + self.data = dict(data) if data else None + self.files = dict(files) if files else None + self.headers = {k: v for k, v in (headers or {}).items() if v is not None} # A PUT carries a key only when the caller passes one: the signed upload PUT takes none. if (self.method in MUTATING or idempotency_key) and "Idempotency-Key" not in self.headers: self.headers["Idempotency-Key"] = idempotency_key or str(uuid.uuid4()) @@ -199,6 +210,8 @@ def send(self, call: Call) -> httpx.Response: params=call.params, json=call.json, content=call.content, + data=call.data, + files=call.files, headers=call.headers, ) except httpx.TimeoutException as exc: @@ -258,6 +271,8 @@ async def send(self, call: Call) -> httpx.Response: params=call.params, json=call.json, content=call.content, + data=call.data, + files=call.files, headers=call.headers, ) except httpx.TimeoutException as exc: diff --git a/src/faxforagents/async_client.py b/src/faxforagents/async_client.py index 08fdd68..fc73868 100644 --- a/src/faxforagents/async_client.py +++ b/src/faxforagents/async_client.py @@ -27,7 +27,7 @@ from faxforagents import models as m from faxforagents._http import AsyncHttp, Settings, apaginate -from faxforagents.client import guess_content_type +from faxforagents.client import form_fields, guess_content_type, one_time_file, token_header from faxforagents.errors import ConflictError TERMINAL_STATES = frozenset({"delivered", "partial", "failed", "unknown"}) @@ -1309,6 +1309,205 @@ async def revoke_grant(self, grant_id: str, *, idempotency_key: str | None = Non await self._h.request("DELETE", f"/v1/oauth/grants/{grant_id}", idempotency_key=idempotency_key) +class OneTimeSends(_Resource): + """One fax paid by card, with no wallet (the ``one-time-sends`` operations). + + A person pays on the hosted page of ``checkout`` and clicks the link mailed to them. Nothing here + charges a card. The create answers ``status_token`` once. Pass it as ``order_token`` to read the order, + open its checkout and mail the confirmation again from a client that has no key. A key of the order's + organization needs no token.""" + + async def config(self) -> m.OneTimeConfig: + """Whether one-time sends are open, with the card and the limits. No key needed.""" + return await self._h.request("GET", "/v1/one-time-sends/config", model=m.OneTimeConfig) + + async def create( + self, + *, + to: str, + sender_name: str, + accept_aup: bool, + email: str | None = None, + file: bytes | str | Path | None = None, + filename: str | None = None, + content_type: str | None = None, + text: str | None = None, + text_format: str | None = None, + to_name: str | None = None, + subject: str | None = None, + message: str | None = None, + delete_after_send: bool | None = None, + source_ref: str | None = None, + idempotency_key: str | None = None, + ) -> m.OneTimeOrderCreated: + """Create an order from exactly one of ``file`` or ``text``. Nothing is charged or sent. + + ``file`` is the bytes or the path of a PDF, PNG, JPEG or TIFF and goes as multipart. ``text`` is + plain text, or Markdown with ``text_format="markdown"``, and goes as JSON. ``accept_aup=True`` + accepts the acceptable use policy and the terms that ``config`` names. ``email`` is required + without a key. A test key is refused: an order is always live. Scope with a key: fax:send.""" + if (file is None) == (text is None): + raise ValueError("pass exactly one of file or text") + fields: dict[str, Any] = { + "to": to, + "sender_name": sender_name, + "accept_aup": accept_aup, + "email": email, + "to_name": to_name, + "subject": subject, + "message": message, + "delete_after_send": delete_after_send, + "source_ref": source_ref, + } + if file is None: + body = {**fields, "text": text, "text_format": text_format} + return await self._h.request( + "POST", + "/v1/one-time-sends", + model=m.OneTimeOrderCreated, + json=body, + idempotency_key=idempotency_key, + ) + name, data, ctype = one_time_file(file, filename, content_type) + return await self._h.request( + "POST", + "/v1/one-time-sends", + model=m.OneTimeOrderCreated, + data=form_fields(fields), + files={"file": (name, data, ctype)}, + idempotency_key=idempotency_key, + ) + + async def create_from_quote( + self, + *, + quote_id: str, + sender_name: str, + accept_aup: bool, + email: str | None = None, + approval_id: str | None = None, + delete_after_send: bool | None = None, + source_ref: str | None = None, + idempotency_key: str | None = None, + ) -> m.OneTimeOrderCreated: + """Turn a live quote the wallet does not cover into an order. Scope: fax:send, a live key. + + The price is the one-time price of the card, not the quote's. Paying by card does not replace + approval: pass ``approval_id`` unless a delegated policy allows the send, or the answer is 403 + ``approval_required``.""" + body = m.OneTimeFromQuote( + quote_id=quote_id, + sender_name=sender_name, + accept_aup=accept_aup, + email=email, + approval_id=approval_id, + delete_after_send=delete_after_send, + source_ref=source_ref, + ) + return await self._h.request( + "POST", + "/v1/one-time-sends/from-quote", + model=m.OneTimeOrderCreated, + json=body, + idempotency_key=idempotency_key, + ) + + async def list( + self, *, status: str | None = None, cursor: str | None = None, limit: int | None = None + ) -> m.OneTimeOrderPage: + """The organization's orders, newest first. A test key reads an empty list. Scope: fax:read.""" + params = {"status": status, "cursor": cursor, "limit": limit} + return await self._h.request("GET", "/v1/one-time-sends", model=m.OneTimeOrderPage, params=params) + + def iter(self, *, status: str | None = None, limit: int | None = None) -> AsyncIterator[m.OneTimeOrder]: + """Every order, following next_cursor.""" + return apaginate(self.list, {"status": status, "limit": limit}) + + async def get(self, order_id: str, *, order_token: str | None = None) -> m.OneTimeOrder: + """The order: its status, the step that is waiting in ``next``, and the fax once released. + + Read it with a key of the order's organization (scope fax:read) or with ``order_token``.""" + return await self._h.request( + "GET", f"/v1/one-time-sends/{order_id}", model=m.OneTimeOrder, headers=token_header(order_token) + ) + + async def wait( + self, + order_id: str, + *, + until: str | Sequence[str] = "done", + order_token: str | None = None, + timeout: float = 300.0, + interval: float = 3.0, + ) -> m.OneTimeOrder: + """Poll until ``next`` is ``until`` (``done`` by default), or the order is done. + + ``until`` is one of checkout, confirm, wait, done, or several. Returns the last order read, which + may still be short of ``until`` when ``timeout`` ran out. A person pays and confirms, so allow + minutes.""" + wanted = {until} if isinstance(until, str) else set(until) + wanted.add("done") + deadline = time.monotonic() + timeout + order = await self.get(order_id, order_token=order_token) + while order.next not in wanted and time.monotonic() < deadline: + await asyncio.sleep(interval) + order = await self.get(order_id, order_token=order_token) + return order + + async def checkout( + self, order_id: str, *, order_token: str | None = None, idempotency_key: str | None = None + ) -> m.OneTimeCheckout: + """Open the hosted card payment and return its ``checkout_url``. Scope with a key: fax:send. + + The card is authorized on that page and charged only when the carrier confirms delivery. A + second call answers the same page while it can still be paid.""" + return await self._h.request( + "POST", + f"/v1/one-time-sends/{order_id}/checkout", + model=m.OneTimeCheckout, + json={}, + headers=token_header(order_token), + idempotency_key=idempotency_key, + ) + + async def get_by_token(self, token: str) -> m.OneTimeConfirmSummary: + """What a mailed confirmation link is for, without using it. No key needed.""" + return await self._h.request( + "GET", f"/v1/one-time-sends/by-token/{token}", model=m.OneTimeConfirmSummary + ) + + async def confirm(self, order_id: str, *, token: str) -> m.OneTimeConfirmed: + """Confirm the order with the token of the mailed link. The fax is released. No key needed. + + The link works once. A repeat answers 409 ``link_used`` and sends nothing.""" + return await self._h.request( + "POST", + f"/v1/one-time-sends/{order_id}/confirm", + model=m.OneTimeConfirmed, + json=m.OneTimeConfirm(token=token), + ) + + async def resend_confirmation( + self, order_id: str, *, order_token: str | None = None, idempotency_key: str | None = None + ) -> m.OneTimeOrder: + """Mail the confirmation link again. At most three times per order and once in ten minutes.""" + return await self._h.request( + "POST", + f"/v1/one-time-sends/{order_id}/resend-confirmation", + model=m.OneTimeOrder, + json={}, + headers=token_header(order_token), + idempotency_key=idempotency_key, + ) + + async def get_receipt_pdf(self, order_id: str, *, order_token: str | None = None) -> bytes: + """The receipt of the order's fax as a one-page PDF. 409 while the fax has not ended.""" + data = await self._h.request( + "GET", f"/v1/one-time-sends/{order_id}/receipt.pdf", headers=token_header(order_token) + ) + return bytes(data) + + class AsyncClient: """The asynchronous Fax for Agents client. Use it as an async context manager or await ``close``.""" @@ -1322,8 +1521,13 @@ def __init__( backoff_base: float = 0.5, max_retry_after: float = 30.0, transport: httpx.AsyncBaseTransport | None = None, + keyless: bool = False, ) -> None: - self.settings = Settings(api_key, base_url, timeout, max_retries, backoff_base, max_retry_after) + """``keyless=True`` builds a client with no API key. It can call ``one_time_sends`` and nothing + else: every other operation answers 401.""" + self.settings = Settings( + api_key, base_url, timeout, max_retries, backoff_base, max_retry_after, keyless=keyless + ) self._h = AsyncHttp(self.settings, transport) self.identity = Identity(self._h) self.documents = Documents(self._h) @@ -1343,6 +1547,7 @@ def __init__( self.organizations = Organizations(self._h) self.members = Members(self._h) self.oauth = Oauth(self._h) + self.one_time_sends = OneTimeSends(self._h) async def me(self) -> m.Me: """Shortcut for ``identity.me()``.""" diff --git a/src/faxforagents/client.py b/src/faxforagents/client.py index 0bc304a..0da35de 100644 --- a/src/faxforagents/client.py +++ b/src/faxforagents/client.py @@ -27,7 +27,7 @@ import httpx from faxforagents import models as m -from faxforagents._http import Settings, SyncHttp, paginate +from faxforagents._http import ORDER_TOKEN_HEADER, Settings, SyncHttp, paginate from faxforagents.errors import ConflictError TERMINAL_STATES = frozenset({"delivered", "partial", "failed", "unknown"}) @@ -48,6 +48,40 @@ def guess_content_type(filename: str) -> str: return CONTENT_TYPES.get(Path(filename).suffix.lower(), "application/pdf") +#: Content types a one-time order takes as a file, by suffix. Anything else is sent as a PDF. +ONE_TIME_CONTENT_TYPES = {**CONTENT_TYPES, ".txt": "text/plain", ".md": "text/markdown"} + + +def token_header(order_token: str | None) -> dict[str, str] | None: + """The ``X-FFA-Order-Token`` header of a one-time order, or nothing when no token is passed.""" + return {ORDER_TOKEN_HEADER: order_token} if order_token else None + + +def form_fields(fields: dict[str, Any]) -> dict[str, str]: + """Multipart text fields: no empty values, booleans as ``true`` or ``false``.""" + out: dict[str, str] = {} + for name, value in fields.items(): + if value is None: + continue + out[name] = ("true" if value else "false") if isinstance(value, bool) else str(value) + return out + + +def one_time_file( + file: bytes | str | Path, filename: str | None, content_type: str | None +) -> tuple[str, bytes, str]: + """``(filename, bytes, content_type)`` of the document of a one-time order, from bytes or a path.""" + if isinstance(file, (str, Path)): + path = Path(file) + data = path.read_bytes() + name = filename or path.name + else: + data = bytes(file) + name = filename or "document.pdf" + ctype = content_type or ONE_TIME_CONTENT_TYPES.get(Path(name).suffix.lower(), "application/pdf") + return name, data, ctype + + class _Resource: def __init__(self, http: SyncHttp) -> None: self._h = http @@ -1296,6 +1330,203 @@ def revoke_grant(self, grant_id: str, *, idempotency_key: str | None = None) -> self._h.request("DELETE", f"/v1/oauth/grants/{grant_id}", idempotency_key=idempotency_key) +class OneTimeSends(_Resource): + """One fax paid by card, with no wallet (the ``one-time-sends`` operations). + + A person pays on the hosted page of ``checkout`` and clicks the link mailed to them. Nothing here + charges a card. The create answers ``status_token`` once. Pass it as ``order_token`` to read the order, + open its checkout and mail the confirmation again from a client that has no key. A key of the order's + organization needs no token.""" + + def config(self) -> m.OneTimeConfig: + """Whether one-time sends are open, with the card and the limits. No key needed.""" + return self._h.request("GET", "/v1/one-time-sends/config", model=m.OneTimeConfig) + + def create( + self, + *, + to: str, + sender_name: str, + accept_aup: bool, + email: str | None = None, + file: bytes | str | Path | None = None, + filename: str | None = None, + content_type: str | None = None, + text: str | None = None, + text_format: str | None = None, + to_name: str | None = None, + subject: str | None = None, + message: str | None = None, + delete_after_send: bool | None = None, + source_ref: str | None = None, + idempotency_key: str | None = None, + ) -> m.OneTimeOrderCreated: + """Create an order from exactly one of ``file`` or ``text``. Nothing is charged or sent. + + ``file`` is the bytes or the path of a PDF, PNG, JPEG or TIFF and goes as multipart. ``text`` is + plain text, or Markdown with ``text_format="markdown"``, and goes as JSON. ``accept_aup=True`` + accepts the acceptable use policy and the terms that ``config`` names. ``email`` is required + without a key. A test key is refused: an order is always live. Scope with a key: fax:send.""" + if (file is None) == (text is None): + raise ValueError("pass exactly one of file or text") + fields: dict[str, Any] = { + "to": to, + "sender_name": sender_name, + "accept_aup": accept_aup, + "email": email, + "to_name": to_name, + "subject": subject, + "message": message, + "delete_after_send": delete_after_send, + "source_ref": source_ref, + } + if file is None: + body = {**fields, "text": text, "text_format": text_format} + return self._h.request( + "POST", + "/v1/one-time-sends", + model=m.OneTimeOrderCreated, + json=body, + idempotency_key=idempotency_key, + ) + name, data, ctype = one_time_file(file, filename, content_type) + return self._h.request( + "POST", + "/v1/one-time-sends", + model=m.OneTimeOrderCreated, + data=form_fields(fields), + files={"file": (name, data, ctype)}, + idempotency_key=idempotency_key, + ) + + def create_from_quote( + self, + *, + quote_id: str, + sender_name: str, + accept_aup: bool, + email: str | None = None, + approval_id: str | None = None, + delete_after_send: bool | None = None, + source_ref: str | None = None, + idempotency_key: str | None = None, + ) -> m.OneTimeOrderCreated: + """Turn a live quote the wallet does not cover into an order. Scope: fax:send, a live key. + + The price is the one-time price of the card, not the quote's. Paying by card does not replace + approval: pass ``approval_id`` unless a delegated policy allows the send, or the answer is 403 + ``approval_required``.""" + body = m.OneTimeFromQuote( + quote_id=quote_id, + sender_name=sender_name, + accept_aup=accept_aup, + email=email, + approval_id=approval_id, + delete_after_send=delete_after_send, + source_ref=source_ref, + ) + return self._h.request( + "POST", + "/v1/one-time-sends/from-quote", + model=m.OneTimeOrderCreated, + json=body, + idempotency_key=idempotency_key, + ) + + def list( + self, *, status: str | None = None, cursor: str | None = None, limit: int | None = None + ) -> m.OneTimeOrderPage: + """The organization's orders, newest first. A test key reads an empty list. Scope: fax:read.""" + params = {"status": status, "cursor": cursor, "limit": limit} + return self._h.request("GET", "/v1/one-time-sends", model=m.OneTimeOrderPage, params=params) + + def iter(self, *, status: str | None = None, limit: int | None = None) -> Iterator[m.OneTimeOrder]: + """Every order, following next_cursor.""" + return paginate(lambda p: self.list(**p), {"status": status, "limit": limit}) + + def get(self, order_id: str, *, order_token: str | None = None) -> m.OneTimeOrder: + """The order: its status, the step that is waiting in ``next``, and the fax once released. + + Read it with a key of the order's organization (scope fax:read) or with ``order_token``.""" + return self._h.request( + "GET", f"/v1/one-time-sends/{order_id}", model=m.OneTimeOrder, headers=token_header(order_token) + ) + + def wait( + self, + order_id: str, + *, + until: str | Sequence[str] = "done", + order_token: str | None = None, + timeout: float = 300.0, + interval: float = 3.0, + ) -> m.OneTimeOrder: + """Poll until ``next`` is ``until`` (``done`` by default), or the order is done. + + ``until`` is one of checkout, confirm, wait, done, or several. Returns the last order read, which + may still be short of ``until`` when ``timeout`` ran out. A person pays and confirms, so allow + minutes.""" + wanted = {until} if isinstance(until, str) else set(until) + wanted.add("done") + deadline = time.monotonic() + timeout + order = self.get(order_id, order_token=order_token) + while order.next not in wanted and time.monotonic() < deadline: + time.sleep(interval) + order = self.get(order_id, order_token=order_token) + return order + + def checkout( + self, order_id: str, *, order_token: str | None = None, idempotency_key: str | None = None + ) -> m.OneTimeCheckout: + """Open the hosted card payment and return its ``checkout_url``. Scope with a key: fax:send. + + The card is authorized on that page and charged only when the carrier confirms delivery. A + second call answers the same page while it can still be paid.""" + return self._h.request( + "POST", + f"/v1/one-time-sends/{order_id}/checkout", + model=m.OneTimeCheckout, + json={}, + headers=token_header(order_token), + idempotency_key=idempotency_key, + ) + + def get_by_token(self, token: str) -> m.OneTimeConfirmSummary: + """What a mailed confirmation link is for, without using it. No key needed.""" + return self._h.request("GET", f"/v1/one-time-sends/by-token/{token}", model=m.OneTimeConfirmSummary) + + def confirm(self, order_id: str, *, token: str) -> m.OneTimeConfirmed: + """Confirm the order with the token of the mailed link. The fax is released. No key needed. + + The link works once. A repeat answers 409 ``link_used`` and sends nothing.""" + return self._h.request( + "POST", + f"/v1/one-time-sends/{order_id}/confirm", + model=m.OneTimeConfirmed, + json=m.OneTimeConfirm(token=token), + ) + + def resend_confirmation( + self, order_id: str, *, order_token: str | None = None, idempotency_key: str | None = None + ) -> m.OneTimeOrder: + """Mail the confirmation link again. At most three times per order and once in ten minutes.""" + return self._h.request( + "POST", + f"/v1/one-time-sends/{order_id}/resend-confirmation", + model=m.OneTimeOrder, + json={}, + headers=token_header(order_token), + idempotency_key=idempotency_key, + ) + + def get_receipt_pdf(self, order_id: str, *, order_token: str | None = None) -> bytes: + """The receipt of the order's fax as a one-page PDF. 409 while the fax has not ended.""" + data = self._h.request( + "GET", f"/v1/one-time-sends/{order_id}/receipt.pdf", headers=token_header(order_token) + ) + return bytes(data) + + class Client: """The synchronous Fax for Agents client. Use it as a context manager or call ``close``.""" @@ -1309,8 +1540,13 @@ def __init__( backoff_base: float = 0.5, max_retry_after: float = 30.0, transport: httpx.BaseTransport | None = None, + keyless: bool = False, ) -> None: - self.settings = Settings(api_key, base_url, timeout, max_retries, backoff_base, max_retry_after) + """``keyless=True`` builds a client with no API key. It can call ``one_time_sends`` and nothing + else: every other operation answers 401.""" + self.settings = Settings( + api_key, base_url, timeout, max_retries, backoff_base, max_retry_after, keyless=keyless + ) self._h = SyncHttp(self.settings, transport) self.identity = Identity(self._h) self.documents = Documents(self._h) @@ -1330,6 +1566,7 @@ def __init__( self.organizations = Organizations(self._h) self.members = Members(self._h) self.oauth = Oauth(self._h) + self.one_time_sends = OneTimeSends(self._h) def me(self) -> m.Me: """Shortcut for ``identity.me()``.""" diff --git a/src/faxforagents/errors.py b/src/faxforagents/errors.py index 26b0a83..42de7a3 100644 --- a/src/faxforagents/errors.py +++ b/src/faxforagents/errors.py @@ -72,6 +72,15 @@ class ConflictError(APIError): class PaymentRequiredError(APIError): """402 insufficient_balance: the wallet does not cover the fax. A person tops up.""" + @property + def one_time_offer(self) -> dict[str, Any] | None: + """``details["one_time_offer"]`` when the server sent one, else ``None``. + + A server that offers the card path for this exact send names its price here. Pass the quote to + ``one_time_sends.create_from_quote``. Servers at API 1.16.1 do not send it.""" + offer = self.details.get("one_time_offer") if isinstance(self.details, dict) else None + return offer if isinstance(offer, dict) else None + class RateLimitedError(APIError): """429 rate_limited. ``retry_after`` is the seconds the API asked for.""" diff --git a/src/faxforagents/models.py b/src/faxforagents/models.py index 7fae3d5..6b776ee 100644 --- a/src/faxforagents/models.py +++ b/src/faxforagents/models.py @@ -1,6 +1,6 @@ """Typed models of the Fax for Agents API. -GENERATED by scripts/gen_models.py from spec/openapi.yaml (API version 1.13.2). Do not edit by hand. +GENERATED by scripts/gen_models.py from spec/openapi.yaml (API version 1.16.1). Do not edit by hand. Money fields ending in ``_micro`` are integer USD micro-units (39000 is $0.039). Timestamps are ISO 8601 strings in UTC. Response models accept fields the API adds later. Request models refuse unknown fields. """ @@ -367,8 +367,8 @@ class ApiKeyWithSecret(_Response): expires_at: str | None revoked_at: str | None rotated_from_id: str | None - secret: str = Field(..., description="Shown once. Store it now; it cannot be read back.") connected_app: bool = Field(False, description="The key belongs to a connected app used over stdio, such as the Gemini CLI extension. The organization's auto-send setting (Settings, Connected apps) binds it like an OAuth connection.") + secret: str = Field(..., description="Shown once. Store it now; it cannot be read back.") class ApprovalDecision(_Request): @@ -702,10 +702,10 @@ class Policy(_Response): version: int current_version_id: str | None rules: dict[str, Any] + managed: str | None = Field(None, description="connected_apps when a Settings switch (the auto-send setting) manages this policy. Such a policy is read here and changed only through PUT /v1/organizations/current/auto-send.") created_by: str created_at: str updated_at: str - managed: str | None = Field(None, description="connected_apps when a Settings switch (the auto-send setting) manages this policy. Such a policy is read here and changed only through PUT /v1/organizations/current/auto-send.") class EffectivePolicies(_Response): @@ -713,9 +713,9 @@ class EffectivePolicies(_Response): environment: Literal["live", "test"] under_policy: bool approval_required_without_policy: bool + requires_human_approval: bool = Field(False, description="Server-set for hosted integration clients and their keys: every send needs a person's approval, whatever the policies say.") policies: list[Policy] rules_order: list[str] - requires_human_approval: bool = Field(False, description="Server-set for hosted integration clients and their keys: every send needs a person's approval, whatever the policies say.") class ErrorEnvelope(_Response): @@ -828,6 +828,23 @@ class FaxCreate(_Request): approval_id: str | None = Field(None, description="An approved approval request bound to this quote. Consumed by this one send.") +class OneTimeCard(_Response): + last4: str | None + brand: str | None + + +class OneTimeJob(_Response): + """The order behind a fax job that a one-time send made: what was authorized, what was charged.""" + + order_id: str + status: Literal["draft", "checkout_open", "authorized", "awaiting_confirmation", "released", "capturing", "canceling", "captured", "released_hold", "expired", "refused", "settle_failed"] | None = None + amount_micro: int | None = None + captured_micro: int | None = None + refunded_micro: int | None = Field(None, description="What was refunded to the card after the charge, in micro-units.") + document_pages: int | None = Field(None, description="The pages of the order's document. The cover sheet is not counted.") + card: OneTimeCard | None = None + + class FaxJob(_Response): id: str environment: Literal["live", "test"] @@ -859,14 +876,12 @@ class FaxJob(_Response): completed_at: str | None cancel_requested_at: str | None evidence: dict[str, Any] | None = Field(None, description="The carrier's evidence once delivered or partial; null before.") + one_time_order_id: str | None = Field(None, description="The one-time order this fax was paid by; null for a wallet fax.") + one_time: OneTimeJob | None = None # Returned by GET /v1/faxes/{fax_id}. approval_id: str | None = Field(None, description="Returned by GET /v1/faxes/{fax_id}. Declared here until the public schema includes it.") # Returned by GET /v1/faxes/{fax_id}. policy_version_id: str | None = Field(None, description="Returned by GET /v1/faxes/{fax_id}. Declared here until the public schema includes it.") - # Returned by GET /v1/faxes/{fax_id}. - one_time_order_id: str | None = Field(None, description="Returned by GET /v1/faxes/{fax_id}. Declared here until the public schema includes it.") - # Returned by GET /v1/faxes/{fax_id}. - one_time: dict[str, Any] | None = Field(None, description="Returned by GET /v1/faxes/{fax_id}. Declared here until the public schema includes it.") class FaxJobPage(_Response): @@ -990,6 +1005,12 @@ class InboxUpdate(_Request): status: Literal["received", "read", "archived"] | None = None +class InsufficientBalance(_Response): + """The error envelope of 402 insufficient_balance, with its details written out.""" + + error: dict[str, Any] + + class InventoryNumber(_Response): e164: str country: str @@ -1190,6 +1211,14 @@ class OAuthRedirect(_Response): redirect_to: str = Field(..., description="Where the browser goes next: the client's registered redirect URI.") +class OneTimeCheckout(_Response): + order_id: str + checkout_url: str + expires_at: str + amount_micro: int + currency: str + + class OneTimeRoute(_Response): label: str flat_micro: int @@ -1216,6 +1245,147 @@ class OneTimeConfig(_Response): terms_url: str +class OneTimeConfirm(_Request): + token: str = Field(..., description="The token of the link in the confirmation mail.") + + +class OneTimeConfirmSummary(_Response): + """What the confirm page shows before the click. ``state`` is ``ok`` while the link can still be used, else ``link_used`` or ``link_expired``. Reading it consumes nothing.""" + + order_id: str + state: Literal["ok", "link_used", "link_expired"] + status: Literal["draft", "checkout_open", "authorized", "awaiting_confirmation", "released", "capturing", "canceling", "captured", "released_hold", "expired", "refused", "settle_failed"] + to: str + sender_name: str + email: str + document_pages: int + amount_micro: int + currency: str + confirm_deadline_at: str | None + + +class OneTimePrice(_Response): + price_version: str + currency: str + flat_micro: int + included_pages: int + extra_page_micro: int + cover_included: bool + + +class OneTimeOrder(_Response): + """One order of the one-time send. ``next`` is what the caller does now: ``checkout`` (open the payment), ``confirm`` (the payer clicks the link in the mail), ``wait`` (nothing to do; read again) or ``done``. Money is integer micro-USD. ``captured_micro`` is what the card was charged, written only once the payment provider confirmed it. ``fax`` is the fax job once the order was released, with the carrier's evidence once it has any.""" + + id: str + status: Literal["draft", "checkout_open", "authorized", "awaiting_confirmation", "released", "capturing", "canceling", "captured", "released_hold", "expired", "refused", "settle_failed"] + next: Literal["checkout", "confirm", "wait", "done"] + environment: Literal["live", "test"] + source: Literal["web", "api", "mcp"] + email: str + email_verified: bool + sender_name: str + to: str + route: str + cover: dict[str, Any] | None + document_pages: int + document_sha256: str + document_id: str | None + delete_after_send: bool = Field(False, description="The sender asked for the document and the transmitted copy to be deleted the moment the fax ends in delivered, partial or failed.") + document_deleted: bool = Field(False, description="True once no copy of the document is held: it was deleted after the send, by the retention period or by its owner, or the order ended before it was released and the uploaded file was removed. The order, the fax and the carrier evidence stay; they hold no document content.") + document_expires_at: str | None = Field(None, description="When the retention period deletes the stored document. Null before the release and once the document is deleted.") + price: OneTimePrice + amount_micro: int + captured_micro: int + refunded_micro: int = Field(0, description="What was refunded to the card after the charge, in micro-units, as the ledger holds it. Zero when nothing was refunded. captured_micro stays what was charged.") + card: OneTimeCard | None + checkout_url: str | None + checkout_expires_at: str | None + authorized_at: str | None + confirm_deadline_at: str | None + capture_deadline_at: str | None + confirm_mail_sent_at: str | None + resend_allowed_at: str | None = Field(None, description="When the confirmation mail may be asked for again; null when it may not.") + released_at: str | None + settled_at: str | None + quote_id: str | None + fax_job_id: str | None + failure_code: str | None + aup_version: str + warnings: list[dict[str, Any]] | None = None + fax: dict[str, Any] | None = Field(None, description="The fax job (the FaxJob shape) once the order was released; null before.") + created_at: str + updated_at: str + + +class OneTimeConfirmed(_Response): + """``signed_in`` says whether this browser now holds a session for the payer; ``me`` is then the same object ``GET /v1/auth/me`` answers (the CSRF token included).""" + + order: OneTimeOrder + signed_in: bool + me: dict[str, Any] | None = None + + +class OneTimeFromQuote(_Request): + quote_id: str + sender_name: str = Field(..., description="Printed on the cover sheet. 2 to 120 characters.") + accept_aup: bool + email: str | None = None + approval_id: str | None = Field(None, description="An approved, unused approval request for this quote (POST /v1/approval-requests). A key needs it unless a delegated policy allows the send. A session needs none.") + source_ref: str | None = None + delete_after_send: bool | None = Field(None, description="Delete the document and the transmitted copy the moment the fax ends in delivered, partial or failed. The order, the receipt and the carrier evidence stay; they hold no document content. A fax that ends unknown keeps the document until the carrier reports, or until the retention period ends. Left out, the quote's own delete_after_send applies.") + + +class OneTimeOrderCreated(_Response): + """The create answer: the order plus the status token. The token reads this one order, opens its checkout and asks for the confirmation mail again, with no key and no session, for seven days. Send it as ``X-FFA-Order-Token`` (or ``?t=``). ``status_url`` is the order's page in the app with the token.""" + + id: str + status: Literal["draft", "checkout_open", "authorized", "awaiting_confirmation", "released", "capturing", "canceling", "captured", "released_hold", "expired", "refused", "settle_failed"] + next: Literal["checkout", "confirm", "wait", "done"] + environment: Literal["live", "test"] + source: Literal["web", "api", "mcp"] + email: str + email_verified: bool + sender_name: str + to: str + route: str + cover: dict[str, Any] | None + document_pages: int + document_sha256: str + document_id: str | None + delete_after_send: bool = Field(False, description="The sender asked for the document and the transmitted copy to be deleted the moment the fax ends in delivered, partial or failed.") + document_deleted: bool = Field(False, description="True once no copy of the document is held: it was deleted after the send, by the retention period or by its owner, or the order ended before it was released and the uploaded file was removed. The order, the fax and the carrier evidence stay; they hold no document content.") + document_expires_at: str | None = Field(None, description="When the retention period deletes the stored document. Null before the release and once the document is deleted.") + price: OneTimePrice + amount_micro: int + captured_micro: int + refunded_micro: int = Field(0, description="What was refunded to the card after the charge, in micro-units, as the ledger holds it. Zero when nothing was refunded. captured_micro stays what was charged.") + card: OneTimeCard | None + checkout_url: str | None + checkout_expires_at: str | None + authorized_at: str | None + confirm_deadline_at: str | None + capture_deadline_at: str | None + confirm_mail_sent_at: str | None + resend_allowed_at: str | None = Field(None, description="When the confirmation mail may be asked for again; null when it may not.") + released_at: str | None + settled_at: str | None + quote_id: str | None + fax_job_id: str | None + failure_code: str | None + aup_version: str + warnings: list[dict[str, Any]] | None = None + fax: dict[str, Any] | None = Field(None, description="The fax job (the FaxJob shape) once the order was released; null before.") + created_at: str + updated_at: str + status_token: str + status_url: str + + +class OneTimeOrderPage(_Response): + items: list[OneTimeOrder] + next_cursor: str | None = None + + class Organization(_Response): id: str name: str @@ -1433,12 +1603,12 @@ class Quote(_Response): class QuoteCreate(_Request): document_id: str - quality: Literal["standard", "fine", "superfine", "ultra"] = Field("fine", description="Fax resolution. standard is the fax standard's 204 by 98 dpi, fine (the default) 204 by 196, superfine 204 by 392. ultra asks the carrier for its darkest text processing at the same superfine resolution and takes longer to process: measured 2026-11-09, a page sent at ultra and one at superfine both arrived as 1728 by 4112 pixels, 204 by 391 dpi. The price does not change. The job echoes the level.") - sender_name: str | None = Field(None, description="The station name the carrier prints in the fax header, next to the sending number. Letters, digits, spaces and - _ ~ ! . + only, at most 128 characters. Omit to show the number alone.") - delete_after_send: bool = Field(False, description="Delete the document and the transmitted copy the moment the fax ends in delivered, partial or failed. The job, its receipt and the carrier evidence stay; they hold no document content. A fax that ends unknown keeps the document until a reconcile settles it. Default: keep the document for the retention period.") to: str = Field(..., description="Destination in E.164, for example +12025550123.") from_: str | None = Field(None, alias="from", description="A number you own; omit for the shared pool.") cover: dict[str, Any] | None = Field(None, description="Cover sheet fields: to_name, from_name, subject, message. Counts as a page.") + quality: Literal["standard", "fine", "superfine", "ultra"] = Field("fine", description="Fax resolution. standard is the fax standard's 204 by 98 dpi, fine (the default) 204 by 196, superfine 204 by 392. ultra asks the carrier for its darkest text processing at the same superfine resolution and takes longer to process: measured 2026-11-09, a page sent at ultra and one at superfine both arrived as 1728 by 4112 pixels, 204 by 391 dpi. The price does not change. The job echoes the level.") + sender_name: str | None = Field(None, description="The station name the carrier prints in the fax header, next to the sending number. Letters, digits, spaces and - _ ~ ! . + only, at most 128 characters. Omit to show the number alone.") + delete_after_send: bool = Field(False, description="Delete the document and the transmitted copy the moment the fax ends in delivered, partial or failed. The job, its receipt and the carrier evidence stay; they hold no document content. A fax that ends unknown keeps the document until a reconcile settles it. Default: keep the document for the retention period.") class Reauth(_Request): @@ -1455,6 +1625,7 @@ class Receipt(_Response): document_sha256: str transmitted_sha256: str | None billable_pages: int + cover_pages: int = Field(0, description="How many of the billable pages are the cover sheet: 0 or 1.") pages_sent: int amount_micro: int captured_micro: int @@ -1466,6 +1637,7 @@ class Receipt(_Response): attempts: list[Attempt] note: str generated_at: str + one_time: OneTimeJob | None = Field(None, description="Set on the receipt of a fax that a one-time order paid for. It names the order, its status, the card and the amount the card was authorized for. captured_micro in it is the amount charged, written once the payment provider confirmed the capture. For a card order read the money here. Null for a fax paid from the wallet.") class ReconcileResult(_Response): @@ -1963,6 +2135,8 @@ class faxforagents__auth__schemas__Me(_Response): "ExtractionSchema", "ExtractionSchemaList", "FaxCreate", + "OneTimeCard", + "OneTimeJob", "FaxJob", "FaxJobPage", "Grant", @@ -1974,6 +2148,7 @@ class faxforagents__auth__schemas__Me(_Response): "InboxItemSummary", "InboxPage", "InboxUpdate", + "InsufficientBalance", "InventoryNumber", "Inventory", "Invitation", @@ -1999,8 +2174,17 @@ class faxforagents__auth__schemas__Me(_Response): "OAuthConsent", "OAuthConsentDecision", "OAuthRedirect", + "OneTimeCheckout", "OneTimeRoute", "OneTimeConfig", + "OneTimeConfirm", + "OneTimeConfirmSummary", + "OneTimePrice", + "OneTimeOrder", + "OneTimeConfirmed", + "OneTimeFromQuote", + "OneTimeOrderCreated", + "OneTimeOrderPage", "Organization", "OrganizationList", "OrganizationPatch", diff --git a/tests/fixtures/README.md b/tests/fixtures/README.md index d0601e7..8b47aba 100644 --- a/tests/fixtures/README.md +++ b/tests/fixtures/README.md @@ -14,3 +14,11 @@ Documents, numbers, balance and policy records had no additional fields beyond t Policy evaluations also return usage when a policy applies. Fax jobs, quotes, extraction pages and inbound summaries needed the route-documented additions. Capabilities gains `one_time` from the updated snapshot. + +## One-time sends (`one-time.json`) + +Server source revision: `5bee3b046dbcb5bc0fbf98ae59c1a40a1c8c1434`, API 1.16.1. +`config` and `not_found` were recorded from `https://api.faxforagents.com` on 2026-11-12. Both routes answer without a key. +The other cases follow the serializers `order_json` and `job_block` in `onetime/service.py` and the answers in `api/routes/onetime.py`. +They are synthetic examples, not captured traffic: no order was created and no card was charged to make them. +Ids, addresses, the token and the checkout address are test values. diff --git a/tests/fixtures/one-time.json b/tests/fixtures/one-time.json new file mode 100644 index 0000000..3c34163 --- /dev/null +++ b/tests/fixtures/one-time.json @@ -0,0 +1,625 @@ +[ + { + "name": "config", + "model": "OneTimeConfig", + "route": "GET /v1/one-time-sends/config", + "recorded": "https://api.faxforagents.com/v1/one-time-sends/config on 2026-11-12", + "body": { + "enabled": true, + "reason": null, + "price_version": "launch-v2", + "effective_from": "2026-10-31", + "currency": "USD", + "routes": { + "us_ca": { + "label": "United States and Canada", + "flat_micro": 2490000, + "included_pages": 5, + "extra_page_micro": 490000, + "cover_included": true + } + }, + "max_document_pages": 20, + "max_upload_bytes": 10485760, + "max_text_chars": 200000, + "formats": [ + "application/pdf", + "image/png", + "image/jpeg", + "image/tiff", + "text/plain", + "text/markdown" + ], + "aup_version": "2026-10-28", + "aup_url": "https://faxforagents.com/acceptable-use/", + "terms_url": "https://faxforagents.com/terms/" + } + }, + { + "name": "created", + "model": "OneTimeOrderCreated", + "route": "POST /v1/one-time-sends", + "body": { + "id": "oto_77777777777777777777777777777777", + "status": "draft", + "next": "checkout", + "environment": "live", + "source": "api", + "email": "sender@example.com", + "email_verified": false, + "sender_name": "Dana Example", + "to": "+12025550123", + "route": "us_ca", + "cover": { + "from_name": "Dana Example", + "subject": "Signed form" + }, + "document_pages": 2, + "document_sha256": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "document_id": null, + "delete_after_send": false, + "document_deleted": false, + "document_expires_at": null, + "price": { + "price_version": "launch-v2", + "currency": "USD", + "flat_micro": 2490000, + "included_pages": 5, + "extra_page_micro": 490000, + "cover_included": true + }, + "amount_micro": 2490000, + "captured_micro": 0, + "refunded_micro": 0, + "card": null, + "checkout_url": null, + "checkout_expires_at": null, + "authorized_at": null, + "confirm_deadline_at": null, + "capture_deadline_at": null, + "confirm_mail_sent_at": null, + "released_at": null, + "settled_at": null, + "quote_id": null, + "fax_job_id": null, + "failure_code": null, + "aup_version": "2026-10-28", + "warnings": [], + "fax": null, + "created_at": "2026-11-12T09:00:00Z", + "updated_at": "2026-11-12T09:00:00Z", + "resend_allowed_at": null, + "status_token": "1763000000.0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "status_url": "https://app.faxforagents.com/send/oto_77777777777777777777777777777777?t=1763000000.0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a" + } + }, + { + "name": "draft", + "model": "OneTimeOrder", + "route": "GET /v1/one-time-sends/{one_time_order_id}", + "body": { + "id": "oto_77777777777777777777777777777777", + "status": "draft", + "next": "checkout", + "environment": "live", + "source": "api", + "email": "sender@example.com", + "email_verified": false, + "sender_name": "Dana Example", + "to": "+12025550123", + "route": "us_ca", + "cover": { + "from_name": "Dana Example", + "subject": "Signed form" + }, + "document_pages": 2, + "document_sha256": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "document_id": null, + "delete_after_send": false, + "document_deleted": false, + "document_expires_at": null, + "price": { + "price_version": "launch-v2", + "currency": "USD", + "flat_micro": 2490000, + "included_pages": 5, + "extra_page_micro": 490000, + "cover_included": true + }, + "amount_micro": 2490000, + "captured_micro": 0, + "refunded_micro": 0, + "card": null, + "checkout_url": null, + "checkout_expires_at": null, + "authorized_at": null, + "confirm_deadline_at": null, + "capture_deadline_at": null, + "confirm_mail_sent_at": null, + "released_at": null, + "settled_at": null, + "quote_id": null, + "fax_job_id": null, + "failure_code": null, + "aup_version": "2026-10-28", + "warnings": [], + "fax": null, + "created_at": "2026-11-12T09:00:00Z", + "updated_at": "2026-11-12T09:00:00Z", + "resend_allowed_at": null + } + }, + { + "name": "checkout_open", + "model": "OneTimeOrder", + "route": "GET /v1/one-time-sends/{one_time_order_id}", + "body": { + "id": "oto_77777777777777777777777777777777", + "status": "checkout_open", + "next": "checkout", + "environment": "live", + "source": "api", + "email": "sender@example.com", + "email_verified": false, + "sender_name": "Dana Example", + "to": "+12025550123", + "route": "us_ca", + "cover": { + "from_name": "Dana Example", + "subject": "Signed form" + }, + "document_pages": 2, + "document_sha256": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "document_id": null, + "delete_after_send": false, + "document_deleted": false, + "document_expires_at": null, + "price": { + "price_version": "launch-v2", + "currency": "USD", + "flat_micro": 2490000, + "included_pages": 5, + "extra_page_micro": 490000, + "cover_included": true + }, + "amount_micro": 2490000, + "captured_micro": 0, + "refunded_micro": 0, + "card": null, + "checkout_url": "https://checkout.example.test/c/pay/cs_test_1", + "checkout_expires_at": "2026-11-12T09:31:00Z", + "authorized_at": null, + "confirm_deadline_at": null, + "capture_deadline_at": null, + "confirm_mail_sent_at": null, + "released_at": null, + "settled_at": null, + "quote_id": null, + "fax_job_id": null, + "failure_code": null, + "aup_version": "2026-10-28", + "warnings": [], + "fax": null, + "created_at": "2026-11-12T09:00:00Z", + "updated_at": "2026-11-12T09:01:00Z", + "resend_allowed_at": null + } + }, + { + "name": "awaiting_confirmation", + "model": "OneTimeOrder", + "route": "GET /v1/one-time-sends/{one_time_order_id}", + "body": { + "id": "oto_77777777777777777777777777777777", + "status": "awaiting_confirmation", + "next": "confirm", + "environment": "live", + "source": "api", + "email": "sender@example.com", + "email_verified": false, + "sender_name": "Dana Example", + "to": "+12025550123", + "route": "us_ca", + "cover": { + "from_name": "Dana Example", + "subject": "Signed form" + }, + "document_pages": 2, + "document_sha256": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "document_id": null, + "delete_after_send": false, + "document_deleted": false, + "document_expires_at": null, + "price": { + "price_version": "launch-v2", + "currency": "USD", + "flat_micro": 2490000, + "included_pages": 5, + "extra_page_micro": 490000, + "cover_included": true + }, + "amount_micro": 2490000, + "captured_micro": 0, + "refunded_micro": 0, + "card": { + "last4": "4242", + "brand": "visa" + }, + "checkout_url": null, + "checkout_expires_at": null, + "authorized_at": "2026-11-12T09:03:00Z", + "confirm_deadline_at": "2026-11-13T09:03:00Z", + "capture_deadline_at": "2026-11-18T09:03:00Z", + "confirm_mail_sent_at": "2026-11-12T09:03:05Z", + "released_at": null, + "settled_at": null, + "quote_id": null, + "fax_job_id": null, + "failure_code": null, + "aup_version": "2026-10-28", + "warnings": [], + "fax": null, + "created_at": "2026-11-12T09:00:00Z", + "updated_at": "2026-11-12T09:03:05Z", + "resend_allowed_at": "2026-11-12T09:13:05Z" + } + }, + { + "name": "captured", + "model": "OneTimeOrder", + "route": "GET /v1/one-time-sends/{one_time_order_id}", + "body": { + "id": "oto_77777777777777777777777777777777", + "status": "captured", + "next": "done", + "environment": "live", + "source": "api", + "email": "sender@example.com", + "email_verified": true, + "sender_name": "Dana Example", + "to": "+12025550123", + "route": "us_ca", + "cover": { + "from_name": "Dana Example", + "subject": "Signed form" + }, + "document_pages": 2, + "document_sha256": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "document_id": "doc_99999999999999999999999999999999", + "delete_after_send": false, + "document_deleted": false, + "document_expires_at": "2026-12-12T09:06:00Z", + "price": { + "price_version": "launch-v2", + "currency": "USD", + "flat_micro": 2490000, + "included_pages": 5, + "extra_page_micro": 490000, + "cover_included": true + }, + "amount_micro": 2490000, + "captured_micro": 2490000, + "refunded_micro": 0, + "card": { + "last4": "4242", + "brand": "visa" + }, + "checkout_url": null, + "checkout_expires_at": null, + "authorized_at": "2026-11-12T09:03:00Z", + "confirm_deadline_at": "2026-11-13T09:03:00Z", + "capture_deadline_at": "2026-11-18T09:03:00Z", + "confirm_mail_sent_at": "2026-11-12T09:03:05Z", + "released_at": "2026-11-12T09:06:00Z", + "settled_at": "2026-11-12T09:08:10Z", + "quote_id": "quote_66666666666666666666666666666666", + "fax_job_id": "fax_88888888888888888888888888888888", + "failure_code": null, + "aup_version": "2026-10-28", + "warnings": [], + "fax": { + "id": "fax_88888888888888888888888888888888", + "environment": "live", + "direction": "outbound", + "state": "delivered", + "failure_code": null, + "to": "+12025550123", + "from": null, + "route": "us_ca", + "document_id": "doc_99999999999999999999999999999999", + "document_sha256": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "transmitted_sha256": "efefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefef", + "quote_id": "quote_66666666666666666666666666666666", + "billable_pages": 3, + "cover_pages": 1, + "pages_sent": 3, + "amount_micro": 0, + "captured_micro": 0, + "currency": "USD", + "provider": "telnyx", + "provider_ref": "ref_1", + "attempt_count": 1, + "client_reference": null, + "evidence": { + "provider_status": "delivered", + "pages": 3 + }, + "created_at": "2026-11-12T09:06:00Z", + "updated_at": "2026-11-12T09:08:00Z", + "completed_at": "2026-11-12T09:08:00Z", + "cancel_requested_at": null, + "one_time_order_id": "oto_77777777777777777777777777777777", + "one_time": { + "order_id": "oto_77777777777777777777777777777777", + "status": "captured", + "amount_micro": 2490000, + "captured_micro": 2490000, + "refunded_micro": 0, + "document_pages": 2, + "card": { + "last4": "4242", + "brand": "visa" + } + } + }, + "created_at": "2026-11-12T09:00:00Z", + "updated_at": "2026-11-12T09:08:10Z", + "resend_allowed_at": null + } + }, + { + "name": "checkout", + "model": "OneTimeCheckout", + "route": "POST /v1/one-time-sends/{one_time_order_id}/checkout", + "body": { + "order_id": "oto_77777777777777777777777777777777", + "checkout_url": "https://checkout.example.test/c/pay/cs_test_1", + "expires_at": "2026-11-12T09:31:00Z", + "amount_micro": 2490000, + "currency": "USD" + } + }, + { + "name": "by_token", + "model": "OneTimeConfirmSummary", + "route": "GET /v1/one-time-sends/by-token/{token}", + "body": { + "order_id": "oto_77777777777777777777777777777777", + "state": "ok", + "status": "awaiting_confirmation", + "to": "+12025550123", + "sender_name": "Dana Example", + "email": "sender@example.com", + "document_pages": 2, + "amount_micro": 2490000, + "currency": "USD", + "confirm_deadline_at": "2026-11-13T09:03:00Z" + } + }, + { + "name": "confirmed", + "model": "OneTimeConfirmed", + "route": "POST /v1/one-time-sends/{one_time_order_id}/confirm", + "body": { + "order": { + "id": "oto_77777777777777777777777777777777", + "status": "released", + "next": "wait", + "environment": "live", + "source": "api", + "email": "sender@example.com", + "email_verified": true, + "sender_name": "Dana Example", + "to": "+12025550123", + "route": "us_ca", + "cover": { + "from_name": "Dana Example", + "subject": "Signed form" + }, + "document_pages": 2, + "document_sha256": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "document_id": "doc_99999999999999999999999999999999", + "delete_after_send": false, + "document_deleted": false, + "document_expires_at": "2026-12-12T09:06:00Z", + "price": { + "price_version": "launch-v2", + "currency": "USD", + "flat_micro": 2490000, + "included_pages": 5, + "extra_page_micro": 490000, + "cover_included": true + }, + "amount_micro": 2490000, + "captured_micro": 0, + "refunded_micro": 0, + "card": { + "last4": "4242", + "brand": "visa" + }, + "checkout_url": null, + "checkout_expires_at": null, + "authorized_at": "2026-11-12T09:03:00Z", + "confirm_deadline_at": "2026-11-13T09:03:00Z", + "capture_deadline_at": "2026-11-18T09:03:00Z", + "confirm_mail_sent_at": "2026-11-12T09:03:05Z", + "released_at": "2026-11-12T09:06:00Z", + "settled_at": null, + "quote_id": "quote_66666666666666666666666666666666", + "fax_job_id": "fax_88888888888888888888888888888888", + "failure_code": null, + "aup_version": "2026-10-28", + "warnings": [], + "fax": { + "id": "fax_88888888888888888888888888888888", + "environment": "live", + "direction": "outbound", + "state": "queued", + "failure_code": null, + "to": "+12025550123", + "from": null, + "route": "us_ca", + "document_id": "doc_99999999999999999999999999999999", + "document_sha256": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "transmitted_sha256": null, + "quote_id": "quote_66666666666666666666666666666666", + "billable_pages": 3, + "cover_pages": 1, + "pages_sent": 0, + "amount_micro": 0, + "captured_micro": 0, + "currency": "USD", + "provider": "telnyx", + "provider_ref": null, + "attempt_count": 0, + "client_reference": null, + "evidence": null, + "created_at": "2026-11-12T09:06:00Z", + "updated_at": "2026-11-12T09:08:00Z", + "completed_at": null, + "cancel_requested_at": null, + "one_time_order_id": "oto_77777777777777777777777777777777", + "one_time": { + "order_id": "oto_77777777777777777777777777777777", + "status": "released", + "amount_micro": 2490000, + "captured_micro": 0, + "refunded_micro": 0, + "document_pages": 2, + "card": { + "last4": "4242", + "brand": "visa" + } + } + }, + "created_at": "2026-11-12T09:00:00Z", + "updated_at": "2026-11-12T09:06:00Z", + "resend_allowed_at": null + }, + "signed_in": false, + "me": null + } + }, + { + "name": "page", + "model": "OneTimeOrderPage", + "route": "GET /v1/one-time-sends", + "body": { + "items": [ + { + "id": "oto_77777777777777777777777777777777", + "status": "captured", + "next": "done", + "environment": "live", + "source": "api", + "email": "sender@example.com", + "email_verified": true, + "sender_name": "Dana Example", + "to": "+12025550123", + "route": "us_ca", + "cover": { + "from_name": "Dana Example", + "subject": "Signed form" + }, + "document_pages": 2, + "document_sha256": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "document_id": "doc_99999999999999999999999999999999", + "delete_after_send": false, + "document_deleted": false, + "document_expires_at": "2026-12-12T09:06:00Z", + "price": { + "price_version": "launch-v2", + "currency": "USD", + "flat_micro": 2490000, + "included_pages": 5, + "extra_page_micro": 490000, + "cover_included": true + }, + "amount_micro": 2490000, + "captured_micro": 2490000, + "refunded_micro": 0, + "card": { + "last4": "4242", + "brand": "visa" + }, + "checkout_url": null, + "checkout_expires_at": null, + "authorized_at": "2026-11-12T09:03:00Z", + "confirm_deadline_at": "2026-11-13T09:03:00Z", + "capture_deadline_at": "2026-11-18T09:03:00Z", + "confirm_mail_sent_at": "2026-11-12T09:03:05Z", + "released_at": "2026-11-12T09:06:00Z", + "settled_at": "2026-11-12T09:08:10Z", + "quote_id": "quote_66666666666666666666666666666666", + "fax_job_id": "fax_88888888888888888888888888888888", + "failure_code": null, + "aup_version": "2026-10-28", + "warnings": [], + "fax": { + "id": "fax_88888888888888888888888888888888", + "environment": "live", + "direction": "outbound", + "state": "delivered", + "failure_code": null, + "to": "+12025550123", + "from": null, + "route": "us_ca", + "document_id": "doc_99999999999999999999999999999999", + "document_sha256": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "transmitted_sha256": "efefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefef", + "quote_id": "quote_66666666666666666666666666666666", + "billable_pages": 3, + "cover_pages": 1, + "pages_sent": 3, + "amount_micro": 0, + "captured_micro": 0, + "currency": "USD", + "provider": "telnyx", + "provider_ref": "ref_1", + "attempt_count": 1, + "client_reference": null, + "evidence": { + "provider_status": "delivered", + "pages": 3 + }, + "created_at": "2026-11-12T09:06:00Z", + "updated_at": "2026-11-12T09:08:00Z", + "completed_at": "2026-11-12T09:08:00Z", + "cancel_requested_at": null, + "one_time_order_id": "oto_77777777777777777777777777777777", + "one_time": { + "order_id": "oto_77777777777777777777777777777777", + "status": "captured", + "amount_micro": 2490000, + "captured_micro": 2490000, + "refunded_micro": 0, + "document_pages": 2, + "card": { + "last4": "4242", + "brand": "visa" + } + } + }, + "created_at": "2026-11-12T09:00:00Z", + "updated_at": "2026-11-12T09:08:10Z", + "resend_allowed_at": null + } + ], + "next_cursor": null + } + }, + { + "name": "not_found", + "model": null, + "route": "GET /v1/one-time-sends/{one_time_order_id}", + "recorded": "https://api.faxforagents.com/v1/one-time-sends/oto_doesnotexist with a wrong token on 2026-11-12, status 404", + "body": { + "error": { + "code": "not_found", + "message": "no such order", + "retryable": false, + "correlation_id": "99da85378b9cea4eabbd107f5ed18200" + } + } + } +] diff --git a/tests/test_one_time.py b/tests/test_one_time.py new file mode 100644 index 0000000..602e504 --- /dev/null +++ b/tests/test_one_time.py @@ -0,0 +1,282 @@ +"""The one-time send: one fax paid by card. Requests and answers against fixtures/one-time.json.""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +import httpx +import pytest +import respx + +from faxforagents import AsyncClient, Client, NotFoundError, PaymentRequiredError +from tests.conftest import BASE, KEY, error + +CASES: dict[str, Any] = { + case["name"]: case["body"] + for case in json.loads((Path(__file__).parent / "fixtures/one-time.json").read_text()) +} +ORDER = CASES["draft"]["id"] +TOKEN = CASES["created"]["status_token"] + + +@pytest.fixture +def keyless(api: respx.MockRouter) -> Any: + with Client(base_url=BASE, backoff_base=0.0, max_retry_after=0.0, keyless=True) as c: + yield c + + +def test_config_needs_no_key(keyless: Client, api: respx.MockRouter) -> None: + route = api.get("/v1/one-time-sends/config").respond(200, json=CASES["config"]) + config = keyless.one_time_sends.config() + assert config.enabled is True + assert config.price_version == "launch-v2" + assert config.routes["us_ca"].flat_micro == 2490000 + assert config.routes["us_ca"].included_pages == 5 + assert "authorization" not in route.calls.last.request.headers + + +def test_keyless_client_ignores_the_environment(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("FAXFORAGENTS_API_KEY", KEY) + with Client(keyless=True) as c: + assert c.settings.api_key is None + assert "Authorization" not in c.settings.headers() + + +def test_create_from_text_is_json(keyless: Client, api: respx.MockRouter) -> None: + route = api.post("/v1/one-time-sends").respond(201, json=CASES["created"]) + order = keyless.one_time_sends.create( + to="+12025550123", + sender_name="Dana Example", + accept_aup=True, + email="sender@example.com", + text="# Signed form", + text_format="markdown", + subject="Signed form", + ) + assert order.id == ORDER + assert order.next == "checkout" + assert order.status_token == TOKEN + assert order.price.flat_micro == 2490000 + sent = route.calls.last.request + assert sent.headers["content-type"] == "application/json" + assert json.loads(sent.content) == { + "to": "+12025550123", + "sender_name": "Dana Example", + "accept_aup": True, + "email": "sender@example.com", + "subject": "Signed form", + "text": "# Signed form", + "text_format": "markdown", + } + assert sent.headers["idempotency-key"] + assert "authorization" not in sent.headers + + +def test_create_from_a_file_is_multipart(client: Client, api: respx.MockRouter, tmp_path: Path) -> None: + route = api.post("/v1/one-time-sends").respond(201, json=CASES["created"]) + path = tmp_path / "form.pdf" + path.write_bytes(b"%PDF-1.4 test") + client.one_time_sends.create( + to="+12025550123", + sender_name="Dana Example", + accept_aup=True, + file=path, + delete_after_send=True, + idempotency_key="order-1", + ) + sent = route.calls.last.request + assert sent.headers["content-type"].startswith("multipart/form-data; boundary=") + assert sent.headers["idempotency-key"] == "order-1" + assert sent.headers["authorization"] == f"Bearer {KEY}" + body = sent.content + assert b'name="file"; filename="form.pdf"' in body + assert b"Content-Type: application/pdf" in body + assert b"%PDF-1.4 test" in body + assert b'name="accept_aup"\r\n\r\ntrue' in body + assert b'name="delete_after_send"\r\n\r\ntrue' in body + assert b'name="email"' not in body + + +def test_create_file_bytes_and_content_type_by_suffix(client: Client, api: respx.MockRouter) -> None: + route = api.post("/v1/one-time-sends").respond(201, json=CASES["created"]) + client.one_time_sends.create( + to="+12025550123", sender_name="Dana Example", accept_aup=True, file=b"II*", filename="scan.tiff" + ) + body = route.calls.last.request.content + assert b'filename="scan.tiff"' in body + assert b"Content-Type: image/tiff" in body + + +def test_create_needs_exactly_one_of_file_or_text(client: Client) -> None: + with pytest.raises(ValueError, match="exactly one"): + client.one_time_sends.create(to="+12025550123", sender_name="Dana", accept_aup=True) + with pytest.raises(ValueError, match="exactly one"): + client.one_time_sends.create( + to="+12025550123", sender_name="Dana", accept_aup=True, file=b"x", text="y" + ) + + +def test_create_from_quote(client: Client, api: respx.MockRouter) -> None: + route = api.post("/v1/one-time-sends/from-quote").respond(201, json=CASES["created"]) + order = client.one_time_sends.create_from_quote( + quote_id="quote_" + "6" * 32, sender_name="Dana Example", accept_aup=True, approval_id="apr_1" + ) + assert order.status_url and order.status_url.endswith(TOKEN) + assert json.loads(route.calls.last.request.content) == { + "quote_id": "quote_" + "6" * 32, + "sender_name": "Dana Example", + "accept_aup": True, + "approval_id": "apr_1", + } + + +def test_get_sends_the_order_token(keyless: Client, api: respx.MockRouter) -> None: + route = api.get(f"/v1/one-time-sends/{ORDER}").respond(200, json=CASES["checkout_open"]) + order = keyless.one_time_sends.get(ORDER, order_token=TOKEN) + assert order.status == "checkout_open" + assert order.checkout_url + sent = route.calls.last.request + assert sent.headers["x-ffa-order-token"] == TOKEN + assert "authorization" not in sent.headers + + +def test_get_with_a_key_sends_no_token_header(client: Client, api: respx.MockRouter) -> None: + route = api.get(f"/v1/one-time-sends/{ORDER}").respond(200, json=CASES["captured"]) + order = client.one_time_sends.get(ORDER) + assert order.next == "done" + assert order.captured_micro == 2490000 + assert order.card is not None and order.card.last4 == "4242" + assert order.fax is not None and order.fax["state"] == "delivered" + assert "x-ffa-order-token" not in route.calls.last.request.headers + + +def test_a_wrong_token_is_not_found(keyless: Client, api: respx.MockRouter) -> None: + api.get(f"/v1/one-time-sends/{ORDER}").respond(404, json=CASES["not_found"]) + with pytest.raises(NotFoundError) as exc: + keyless.one_time_sends.get(ORDER, order_token="0.wrong") + assert exc.value.code == "not_found" + + +def test_list_and_iter(client: Client, api: respx.MockRouter) -> None: + route = api.get("/v1/one-time-sends").respond(200, json=CASES["page"]) + page = client.one_time_sends.list(status="captured", limit=10) + assert [o.id for o in page.items] == [ORDER] + assert dict(route.calls.last.request.url.params) == {"status": "captured", "limit": "10"} + assert [o.id for o in client.one_time_sends.iter()] == [ORDER] + + +def test_checkout(keyless: Client, api: respx.MockRouter) -> None: + route = api.post(f"/v1/one-time-sends/{ORDER}/checkout").respond(201, json=CASES["checkout"]) + opened = keyless.one_time_sends.checkout(ORDER, order_token=TOKEN) + assert opened.checkout_url == CASES["checkout"]["checkout_url"] + assert opened.amount_micro == 2490000 + sent = route.calls.last.request + assert sent.headers["x-ffa-order-token"] == TOKEN + assert sent.headers["idempotency-key"] + + +def test_get_by_token_and_confirm(keyless: Client, api: respx.MockRouter) -> None: + api.get("/v1/one-time-sends/by-token/mail-token").respond(200, json=CASES["by_token"]) + summary = keyless.one_time_sends.get_by_token("mail-token") + assert summary.state == "ok" + assert summary.order_id == ORDER + route = api.post(f"/v1/one-time-sends/{ORDER}/confirm").respond(200, json=CASES["confirmed"]) + done = keyless.one_time_sends.confirm(summary.order_id, token="mail-token") + assert done.order.status == "released" + assert done.order.next == "wait" + assert done.signed_in is False + assert json.loads(route.calls.last.request.content) == {"token": "mail-token"} + + +def test_resend_confirmation(keyless: Client, api: respx.MockRouter) -> None: + route = api.post(f"/v1/one-time-sends/{ORDER}/resend-confirmation").respond( + 200, json=CASES["awaiting_confirmation"] + ) + order = keyless.one_time_sends.resend_confirmation(ORDER, order_token=TOKEN) + assert order.next == "confirm" + assert order.resend_allowed_at == "2026-11-12T09:13:05Z" + assert route.calls.last.request.headers["x-ffa-order-token"] == TOKEN + + +def test_receipt_pdf(keyless: Client, api: respx.MockRouter) -> None: + route = api.get(f"/v1/one-time-sends/{ORDER}/receipt.pdf").respond( + 200, content=b"%PDF-1.7 receipt", headers={"content-type": "application/pdf"} + ) + assert keyless.one_time_sends.get_receipt_pdf(ORDER, order_token=TOKEN) == b"%PDF-1.7 receipt" + assert route.calls.last.request.headers["x-ffa-order-token"] == TOKEN + + +def test_wait_follows_next_until_done(keyless: Client, api: respx.MockRouter) -> None: + api.get(f"/v1/one-time-sends/{ORDER}").mock( + side_effect=[ + httpx.Response(200, json=CASES["checkout_open"]), + httpx.Response(200, json=CASES["awaiting_confirmation"]), + httpx.Response(200, json=CASES["captured"]), + ] + ) + order = keyless.one_time_sends.wait(ORDER, order_token=TOKEN, interval=0.0) + assert order.status == "captured" + assert order.next == "done" + + +def test_wait_until_a_step(keyless: Client, api: respx.MockRouter) -> None: + route = api.get(f"/v1/one-time-sends/{ORDER}").mock( + side_effect=[ + httpx.Response(200, json=CASES["checkout_open"]), + httpx.Response(200, json=CASES["awaiting_confirmation"]), + httpx.Response(200, json=CASES["captured"]), + ] + ) + order = keyless.one_time_sends.wait(ORDER, until="confirm", order_token=TOKEN, interval=0.0) + assert order.next == "confirm" + assert route.call_count == 2 + + +def test_wait_returns_the_last_order_on_timeout(keyless: Client, api: respx.MockRouter) -> None: + api.get(f"/v1/one-time-sends/{ORDER}").respond(200, json=CASES["checkout_open"]) + order = keyless.one_time_sends.wait(ORDER, order_token=TOKEN, timeout=0.0, interval=0.0) + assert order.next == "checkout" + + +def test_insufficient_balance_one_time_offer(client: Client, api: respx.MockRouter) -> None: + plain = error("insufficient_balance", details={"available_micro": 0, "needed_micro": 78000}) + api.post("/v1/faxes").respond(402, json=plain) + with pytest.raises(PaymentRequiredError) as exc: + client.faxes.create(quote_id="quote_" + "6" * 32) + assert exc.value.details["needed_micro"] == 78000 + assert exc.value.one_time_offer is None + offer = {"amount_micro": 2490000, "currency": "USD", "document_pages": 2} + with_offer = error( + "insufficient_balance", details={"available_micro": 0, "needed_micro": 78000, "one_time_offer": offer} + ) + api.post("/v1/faxes").respond(402, json=with_offer) + with pytest.raises(PaymentRequiredError) as exc: + client.faxes.create(quote_id="quote_" + "6" * 32) + assert exc.value.one_time_offer == offer + + +async def test_async_flow(api: respx.MockRouter) -> None: + api.post("/v1/one-time-sends").respond(201, json=CASES["created"]) + api.post(f"/v1/one-time-sends/{ORDER}/checkout").respond(201, json=CASES["checkout"]) + read = api.get(f"/v1/one-time-sends/{ORDER}").mock( + side_effect=[ + httpx.Response(200, json=CASES["awaiting_confirmation"]), + httpx.Response(200, json=CASES["captured"]), + ] + ) + async with AsyncClient(base_url=BASE, backoff_base=0.0, max_retry_after=0.0, keyless=True) as c: + order = await c.one_time_sends.create( + to="+12025550123", + sender_name="Dana Example", + accept_aup=True, + email="s@example.com", + file=b"%PDF", + ) + opened = await c.one_time_sends.checkout(order.id, order_token=order.status_token) + assert opened.checkout_url + done = await c.one_time_sends.wait(order.id, order_token=order.status_token, interval=0.0) + assert done.next == "done" + assert read.calls.last.request.headers["x-ffa-order-token"] == TOKEN + assert "authorization" not in read.calls.last.request.headers diff --git a/tests/test_response_shapes.py b/tests/test_response_shapes.py index d3204fc..cc03ffa 100644 --- a/tests/test_response_shapes.py +++ b/tests/test_response_shapes.py @@ -10,7 +10,9 @@ from faxforagents import models -CASES = json.loads((Path(__file__).parent / "fixtures/responses.json").read_text()) +FIXTURES = Path(__file__).parent / "fixtures" +CASES = json.loads((FIXTURES / "responses.json").read_text()) +CASES += [case for case in json.loads((FIXTURES / "one-time.json").read_text()) if case["model"]] @pytest.mark.parametrize("case", CASES, ids=lambda case: case["model"]) diff --git a/tests/test_surface.py b/tests/test_surface.py index 851330e..42cba81 100644 --- a/tests/test_surface.py +++ b/tests/test_surface.py @@ -28,9 +28,14 @@ "organizations", "members", "oauth", + "one_time_sends", ] +def class_name(resource: str) -> str: + return "".join(part.capitalize() for part in resource.split("_")) + + def public_methods(cls: type) -> dict[str, inspect.Signature]: out = {} for name, fn in inspect.getmembers(cls, inspect.isfunction): @@ -43,8 +48,8 @@ def public_methods(cls: type) -> dict[str, inspect.Signature]: def test_resources_match() -> None: for name in RESOURCES: - sync_cls = getattr(client, name.capitalize()) - async_cls = getattr(async_client, name.capitalize()) + sync_cls = getattr(client, class_name(name)) + async_cls = getattr(async_client, class_name(name)) s, a = public_methods(sync_cls), public_methods(async_cls) assert s.keys() == a.keys(), name for method in s: @@ -193,6 +198,16 @@ def test_every_operation_has_a_method() -> None: "revoke_invitation": "members.revoke_invitation", "list_oauth_grants": "oauth.list_grants", "revoke_oauth_grant": "oauth.revoke_grant", + "get_one_time_config": "one_time_sends.config", + "create_one_time_order": "one_time_sends.create", + "create_one_time_order_from_quote": "one_time_sends.create_from_quote", + "list_one_time_orders": "one_time_sends.list", + "get_one_time_order": "one_time_sends.get", + "get_one_time_order_by_token": "one_time_sends.get_by_token", + "create_one_time_checkout": "one_time_sends.checkout", + "confirm_one_time_order": "one_time_sends.confirm", + "resend_one_time_confirmation": "one_time_sends.resend_confirmation", + "get_one_time_receipt_pdf": "one_time_sends.get_receipt_pdf", } ids = {op["operationId"] for ops in spec["paths"].values() for op in ops.values()} assert ids == set(covered), ids ^ set(covered)