{
  "openapi": "3.1.0",
  "info": {
    "title": "FaxSeal API",
    "version": "1.0.0",
    "description": "FaxSeal fax API. Users authenticate via OAuth — no manual API token needed. Bearer tokens are issued automatically after the user signs in.\n\n## Connection-first rule\n\n- Before sending a fax, or retrying after any failure, call `getConnectionStatus` if connection has not been confirmed in this conversation.\n- If any endpoint returns `401`, tell the user: \"Your FaxSeal account isn't connected. Click the FS icon or GPT name at the top of this chat, select 'Sign in to FaxSeal', and log in. No account? Sign up free at faxseal.com.\" Do not retry.\n\n## Tool selection guide\n\n- Compose/write/dictate a letter or message → `sendFaxFromText` (PREFERRED)\n- User provides a PDF URL → `sendFaxFromUrl`\n- User uploads a PDF file → `sendFaxFromFile`\n- Delivery check → `getFaxStatus`\n\nDo NOT use `sendFaxFromFile` for text you compose — you cannot generate binary PDF files. Always use `sendFaxFromText` for that.\n\nIf `sendFaxFromFile` fails with a multipart error, that is a tool-selection mistake. Only retry with `sendFaxFromText` if the user is authenticated and wants to proceed."
  },
  "servers": [
    { "url": "https://faxseal.com" }
  ],
  "security": [
    { "bearerAuth": [] }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Bearer token issued automatically via OAuth sign-in"
      }
    },
    "schemas": {
      "FaxQueued": {
        "type": "object",
        "properties": {
          "id":                { "type": "string", "description": "Fax job ID — use with GET /api/v1/fax/{jobId} to track status" },
          "status":            { "type": "string", "enum": ["queued"], "description": "Always 'queued' on creation" },
          "to_number":         { "type": "string", "description": "Destination fax number in E.164 format" },
          "pages":             { "type": "integer", "description": "Number of pages in the fax" },
          "credits_used":      { "type": "integer", "description": "Credits deducted for this fax" },
          "credits_remaining": { "type": "integer", "description": "Credits remaining after this fax" },
          "track_url":         { "type": "string", "format": "uri", "description": "Public tracking URL for this fax" }
        },
        "required": ["id", "status", "to_number", "pages", "credits_used", "credits_remaining", "track_url"]
      },
      "FaxStatus": {
        "type": "object",
        "properties": {
          "jobId":          { "type": "string" },
          "status":         { "type": "string", "enum": ["queued", "sending", "delivered", "failed"], "description": "Current fax status" },
          "toNumber":       { "type": "string" },
          "pages":          { "type": "integer" },
          "creditsUsed":    { "type": "integer" },
          "failureReason":  { "type": "string", "description": "Only present when status is 'failed'" },
          "refundStatus":   { "type": "string", "description": "Only present when a refund was issued" },
          "certReceiptUrl": { "type": "string", "format": "uri", "description": "CERTIFIED DELIVERY RECEIPT — a tamper-evident PDF proving the fax was delivered. Present only when status is 'delivered'. ALWAYS display this as a prominent download link to the user labeled '📋 Download Certified Delivery Receipt'." },
          "createdAt":      { "type": "string", "format": "date-time" },
          "updatedAt":      { "type": "string", "format": "date-time" }
        },
        "required": ["jobId", "status", "toNumber", "pages", "createdAt", "updatedAt"]
      },
      "ConnectionStatus": {
        "type": "object",
        "properties": {
          "connected": { "type": "boolean", "description": "Always true on a successful authenticated response" },
          "id": { "type": "string", "description": "Connected FaxSeal user ID" },
          "email": { "type": "string", "description": "Email address of the connected FaxSeal account" },
          "credits": { "type": "integer", "description": "Available credits on the connected account" }
        },
        "required": ["connected", "id", "email", "credits"]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string" }
        },
        "required": ["error"]
      }
    }
  },
  "paths": {
    "/api/v1/fax/text": {
      "post": {
        "operationId": "sendFaxFromText",
        "x-openai-isConsequential": true,
        "summary": "Compose and send a fax from plain text (USE THIS FOR LETTERS AND MESSAGES)",
        "description": "USE THIS for any letter, complaint, or message the user wants to compose and fax. Converts plain text to PDF and sends it. Do NOT use sendFaxFromFile — you cannot generate binary PDF files. Only call after account connection is confirmed via getConnectionStatus.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["to", "content"],
                "properties": {
                  "to":           { "type": "string", "description": "Destination fax number in E.164 format, e.g. +12025551234" },
                  "content":      { "type": "string", "description": "Full text of the letter or message to fax (plain text, max 50,000 characters)" },
                  "subject":      { "type": "string", "description": "Subject line printed at the top of the document (optional)" },
                  "from_name":    { "type": "string", "description": "Sender name (optional)" },
                  "from_company": { "type": "string", "description": "Sender company (optional)" }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Fax queued successfully",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/FaxQueued" }
              }
            }
          },
          "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Unauthorized — the user's FaxSeal account is not connected or the session has expired. Tell the user: 'Your FaxSeal account isn't connected. Please sign in using the plugin sign-in button. If you don't have an account yet, sign up free at faxseal.com — no credit card required.' Do not retry the request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "402": { "description": "Insufficient credits", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/zapier/send": {
      "post": {
        "operationId": "sendFaxFromUrl",
        "x-openai-isConsequential": true,
        "summary": "Send a fax from a PDF URL",
        "description": "Use when the user provides a URL to an existing PDF. Not for composing letters — use sendFaxFromText for that. Idempotent: retrying same to+file_url within 5 minutes returns the original response without re-charging. Only call after account connection confirmed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["to", "file_url"],
                "properties": {
                  "to":           { "type": "string", "description": "Destination fax number in E.164 format, e.g. +12025551234" },
                  "file_url":     { "type": "string", "format": "uri", "description": "Publicly accessible HTTPS URL to a PDF file (Google Drive, Dropbox, etc.)" },
                  "from_name":    { "type": "string", "description": "Sender name for the cover page (optional)" },
                  "from_company": { "type": "string", "description": "Sender company for the cover page (optional)" },
                  "subject":      { "type": "string", "description": "Fax subject line (optional)" },
                  "notes":        { "type": "string", "description": "Cover page notes (optional)" }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Fax queued successfully",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/FaxQueued" }
              }
            }
          },
          "400": { "description": "Invalid request (bad phone number, invalid URL, file not a PDF, etc.)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Unauthorized — the user's FaxSeal account is not connected or the session has expired. Tell the user: 'Your FaxSeal account isn't connected. Please sign in using the plugin sign-in button. If you don't have an account yet, sign up free at faxseal.com — no credit card required.' Do not retry the request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "402": { "description": "Insufficient credits", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Rate limit exceeded (60 faxes per hour)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/fax-from-file": {
      "post": {
        "operationId": "sendFaxFromFile",
        "x-openai-isConsequential": true,
        "summary": "Send a fax by uploading a PDF file",
        "description": "ONLY use when the user has explicitly uploaded a PDF file in this conversation. Do NOT use for letters or text you compose — use sendFaxFromText instead. You cannot generate binary PDF files. Only call after account connection is confirmed via getConnectionStatus.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["file", "toNumber"],
                "properties": {
                  "file":     { "type": "string", "format": "binary", "description": "PDF file to fax" },
                  "toNumber": { "type": "string", "description": "Destination fax number in E.164 format, e.g. +12025551234" },
                  "note":     { "type": "string", "description": "Optional note prepended as a cover page" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Fax queued successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobId":             { "type": "string" },
                    "status":            { "type": "string", "enum": ["queued"] },
                    "pages":             { "type": "integer" },
                    "trackUrl":          { "type": "string", "format": "uri" },
                    "creditsRemaining":  { "type": "integer" }
                  }
                }
              }
            }
          },
          "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Unauthorized — the user's FaxSeal account is not connected or the session has expired. Tell the user: 'Your FaxSeal account isn't connected. Please sign in using the plugin sign-in button. If you don't have an account yet, sign up free at faxseal.com — no credit card required.' Do not retry the request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "402": { "description": "Insufficient credits", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "415": { "description": "Wrong tool for the current input. This happens when the caller tried to send composed text or a non-upload payload to the binary file-upload endpoint. Do not tell the user the fax is being retried yet. Only switch to sendFaxFromText if the account connection is already confirmed and the user still wants to send the letter.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/zapier/auth": {
      "get": {
        "operationId": "getConnectionStatus",
        "summary": "Check whether the user's FaxSeal account is connected",
        "description": "Use this before sending a fax if account connection has not already been confirmed in the current conversation, and before attempting any retry after a failed send. A successful response confirms the account is connected and returns basic account context.",
        "responses": {
          "200": {
            "description": "Connected account",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ConnectionStatus" }
              }
            }
          },
          "401": { "description": "Unauthorized — the user's FaxSeal account is not connected or the session has expired. Tell the user: 'Your FaxSeal account isn't connected. Please sign in using the plugin sign-in button. If you don't have an account yet, sign up free at faxseal.com — no credit card required.' Do not attempt to send or retry a fax.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/fax/{jobId}": {
      "get": {
        "operationId": "getFaxStatus",
        "summary": "Get fax status",
        "description": "Returns the current status of a fax job. Poll this endpoint after sending to check for delivery. Status progresses: queued → sending → delivered or failed.",
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "schema": { "type": "string" },
            "description": "Fax job ID returned by a send endpoint"
          }
        ],
        "responses": {
          "200": {
            "description": "Fax status",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/FaxStatus" }
              }
            }
          },
          "401": { "description": "Unauthorized — the user's FaxSeal account is not connected or the session has expired. Tell the user: 'Your FaxSeal account isn't connected. Please sign in using the plugin sign-in button. If you don't have an account yet, sign up free at faxseal.com — no credit card required.' Do not retry the request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "Job not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/user/credits": {
      "get": {
        "operationId": "getCredits",
        "summary": "Get credit balance",
        "description": "Returns the current credit balance for the authenticated token.",
        "responses": {
          "200": {
            "description": "Credit balance",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "credits": { "type": "integer", "description": "Available credits" }
                  },
                  "required": ["credits"]
                }
              }
            }
          },
          "401": { "description": "Unauthorized — the user's FaxSeal account is not connected or the session has expired. Tell the user: 'Your FaxSeal account isn't connected. Please sign in using the plugin sign-in button. If you don't have an account yet, sign up free at faxseal.com — no credit card required.' Do not retry the request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    }
  }
}
