{
  "openapi": "3.1.0",
  "info": {
    "title": "InboundX API",
    "version": "1.2.0",
    "summary": "Send pay-per-call traffic to InboundX, connect an AI app to your publisher company on InboundX, and read a workspace's own data.",
    "description": "The public InboundX API for publishers: ping for a real-time bid, attach your call ID, inspect a ping, and receive signed webhooks. The InboundX MCP server lets an AI app act for a publisher company after OAuth sign-in. A workspace reads and changes its own data with a workspace API key. Every page of the docs is at https://app.inboundx.io/docs/publishers, and as Markdown from https://app.inboundx.io/llms.txt.",
    "contact": {
      "name": "InboundX docs",
      "url": "https://app.inboundx.io/docs/publishers"
    }
  },
  "externalDocs": {
    "description": "InboundX publisher docs",
    "url": "https://app.inboundx.io/docs/publishers"
  },
  "servers": [
    {
      "url": "https://app.inboundx.io"
    }
  ],
  "tags": [
    {
      "name": "Real-time bidding",
      "description": "Ping for a bid, attach your call ID and inspect a ping. Authenticated by your ping key."
    },
    {
      "name": "MCP",
      "description": "The InboundX MCP server, JSON-RPC 2.0 over HTTP. Authenticated by OAuth."
    },
    {
      "name": "Workspace API",
      "description": "Read and change a workspace's own data: its calls, campaigns and totals, and every AI connector tool. Authenticated by a workspace API key with a read, write or read_write scope, on the workspace's own address."
    },
    {
      "name": "Discovery",
      "description": "This document."
    }
  ],
  "paths": {
    "/api/rtb/ping": {
      "get": {
        "operationId": "pingGet",
        "tags": [
          "Real-time bidding"
        ],
        "summary": "Ping for a bid",
        "description": "Ask whether we take a caller before you send the call. Every answer is HTTP 200 with JSON: a bid, or a no-bid. Send the parameters in the query string, as a form, or as JSON. Names aren't case-sensitive. When a name is in both the query string and the body, the query string wins. Every answer is HTTP 200: a bid, or a no-bid with a reason (https://app.inboundx.io/docs/errors). Docs: https://app.inboundx.io/docs/ping.",
        "security": [
          {
            "pingKey": []
          }
        ],
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": true,
            "description": "Your ping key for this campaign.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "publisher_id",
            "in": "query",
            "required": false,
            "description": "Your publisher ID. When you send it, it must match the key. Also read as: publisher, afid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "caller_number",
            "in": "query",
            "required": true,
            "description": "The caller's phone number, ideally with the country code. We match the call to its bid by this number when it arrives. Also read as: caller_id, callerid, from, ani. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "caller_state",
            "in": "query",
            "required": false,
            "description": "The caller's US state: its two-letter code or its full name (\"TX\" or \"Texas\"). If you leave it out, or we can't read it, we work it out from the zip, then from the area code. Also read as: state. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 32
            }
          },
          {
            "name": "request_id",
            "in": "query",
            "required": false,
            "description": "Your own ID for this call. We send it back in your postback as {CALL_ID}, and a repeat ping with the same ID gets the same bid back. Also read as: ringba-requestid, ringba_requestid, call_id, callid, sourcecallid, source_call_id, trace, external_id, clickid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "caller_zip",
            "in": "query",
            "required": false,
            "description": "The caller's five-digit ZIP code (ZIP+4 is fine; we keep the first five). If you don't send a state we can read, the zip decides it before the area code does. Also read as: zip, zipcode, zip_code, postal_code, inboundzipcode. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 16
            }
          },
          {
            "name": "caller_city",
            "in": "query",
            "required": false,
            "description": "The caller's city, shown on the call. Letters, spaces and .'- only, up to 64 characters. Also read as: city. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "caller_npa_nxx",
            "in": "query",
            "required": false,
            "description": "The first three or six digits of the caller's number. Shown for context only; a call is never matched by it. Also read as: npanxx, caller_prefix. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "age",
            "in": "query",
            "required": false,
            "description": "The caller's age, a whole number from 18 to 120. Also read as: caller_age. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "dob",
            "in": "query",
            "required": false,
            "description": "The caller's date of birth, as year-month-day or month/day/year. The agent sees the age, not the date. Also read as: date_of_birth. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "homeowner",
            "in": "query",
            "required": false,
            "description": "Whether the caller owns their home: yes or no. Also read as: home_owner, owns_home. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "insured",
            "in": "query",
            "required": false,
            "description": "Whether the caller has insurance now: yes or no. Also read as: currently_insured, has_insurance. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "current_carrier",
            "in": "query",
            "required": false,
            "description": "The caller's current insurer, up to 64 characters. Also read as: current_insurer. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "debt_amount",
            "in": "query",
            "required": false,
            "description": "The caller's debt, in whole dollars. A dollar sign and thousands separators are fine. Also read as: unsecured_debt, debt. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "destination_zip",
            "in": "query",
            "required": false,
            "description": "Where the caller is moving to: a five-digit ZIP code. Also read as: to_zip, move_to_zip. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "loan_amount",
            "in": "query",
            "required": false,
            "description": "The loan or mortgage balance, in whole dollars. Also read as: mortgage_balance. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "accident_date",
            "in": "query",
            "required": false,
            "description": "The date of the accident, as year-month-day or month/day/year. Also read as: incident_date. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "has_attorney",
            "in": "query",
            "required": false,
            "description": "Whether the caller already has an attorney: yes or no. Also read as: attorney, represented. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "trusted_form_cert_url",
            "in": "query",
            "required": false,
            "description": "The address of the caller's consent certificate, on https. Kept with the call for the buyer; never shown to the agent. Also read as: trustedform_cert_url, trusted_form_cert, trusted_form, trustedform, xxtrustedformcerturl. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "jornaya_leadid",
            "in": "query",
            "required": false,
            "description": "Your lead's universal ID. Kept with the call; never shown to the agent. Also read as: jornaya_lead_id, jornayaleadid, jleadid, leadid_token, universal_leadid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "sub_id",
            "in": "query",
            "required": false,
            "description": "Your own ID for the traffic source, up to 128 characters. Kept with the call; never shown to the agent. Also read as: subid, sub1, source_id. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A bid, or a no-bid. Always HTTP 200.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PingAnswer"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "pingPost",
        "tags": [
          "Real-time bidding"
        ],
        "summary": "Ping for a bid",
        "description": "Ask whether we take a caller before you send the call. Every answer is HTTP 200 with JSON: a bid, or a no-bid. Send the parameters in the query string, as a form, or as JSON. Names aren't case-sensitive. When a name is in both the query string and the body, the query string wins. Every answer is HTTP 200: a bid, or a no-bid with a reason (https://app.inboundx.io/docs/errors). Docs: https://app.inboundx.io/docs/ping.",
        "security": [
          {
            "pingKey": []
          }
        ],
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": false,
            "description": "Your ping key for this campaign.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "publisher_id",
            "in": "query",
            "required": false,
            "description": "Your publisher ID. When you send it, it must match the key. Also read as: publisher, afid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "caller_number",
            "in": "query",
            "required": false,
            "description": "The caller's phone number, ideally with the country code. We match the call to its bid by this number when it arrives. Also read as: caller_id, callerid, from, ani. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "caller_state",
            "in": "query",
            "required": false,
            "description": "The caller's US state: its two-letter code or its full name (\"TX\" or \"Texas\"). If you leave it out, or we can't read it, we work it out from the zip, then from the area code. Also read as: state. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 32
            }
          },
          {
            "name": "request_id",
            "in": "query",
            "required": false,
            "description": "Your own ID for this call. We send it back in your postback as {CALL_ID}, and a repeat ping with the same ID gets the same bid back. Also read as: ringba-requestid, ringba_requestid, call_id, callid, sourcecallid, source_call_id, trace, external_id, clickid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "caller_zip",
            "in": "query",
            "required": false,
            "description": "The caller's five-digit ZIP code (ZIP+4 is fine; we keep the first five). If you don't send a state we can read, the zip decides it before the area code does. Also read as: zip, zipcode, zip_code, postal_code, inboundzipcode. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 16
            }
          },
          {
            "name": "caller_city",
            "in": "query",
            "required": false,
            "description": "The caller's city, shown on the call. Letters, spaces and .'- only, up to 64 characters. Also read as: city. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "caller_npa_nxx",
            "in": "query",
            "required": false,
            "description": "The first three or six digits of the caller's number. Shown for context only; a call is never matched by it. Also read as: npanxx, caller_prefix. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "age",
            "in": "query",
            "required": false,
            "description": "The caller's age, a whole number from 18 to 120. Also read as: caller_age. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "dob",
            "in": "query",
            "required": false,
            "description": "The caller's date of birth, as year-month-day or month/day/year. The agent sees the age, not the date. Also read as: date_of_birth. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "homeowner",
            "in": "query",
            "required": false,
            "description": "Whether the caller owns their home: yes or no. Also read as: home_owner, owns_home. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "insured",
            "in": "query",
            "required": false,
            "description": "Whether the caller has insurance now: yes or no. Also read as: currently_insured, has_insurance. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "current_carrier",
            "in": "query",
            "required": false,
            "description": "The caller's current insurer, up to 64 characters. Also read as: current_insurer. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "debt_amount",
            "in": "query",
            "required": false,
            "description": "The caller's debt, in whole dollars. A dollar sign and thousands separators are fine. Also read as: unsecured_debt, debt. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "destination_zip",
            "in": "query",
            "required": false,
            "description": "Where the caller is moving to: a five-digit ZIP code. Also read as: to_zip, move_to_zip. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "loan_amount",
            "in": "query",
            "required": false,
            "description": "The loan or mortgage balance, in whole dollars. Also read as: mortgage_balance. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "accident_date",
            "in": "query",
            "required": false,
            "description": "The date of the accident, as year-month-day or month/day/year. Also read as: incident_date. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "has_attorney",
            "in": "query",
            "required": false,
            "description": "Whether the caller already has an attorney: yes or no. Also read as: attorney, represented. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "trusted_form_cert_url",
            "in": "query",
            "required": false,
            "description": "The address of the caller's consent certificate, on https. Kept with the call for the buyer; never shown to the agent. Also read as: trustedform_cert_url, trusted_form_cert, trusted_form, trustedform, xxtrustedformcerturl. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "jornaya_leadid",
            "in": "query",
            "required": false,
            "description": "Your lead's universal ID. Kept with the call; never shown to the agent. Also read as: jornaya_lead_id, jornayaleadid, jleadid, leadid_token, universal_leadid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "sub_id",
            "in": "query",
            "required": false,
            "description": "Your own ID for the traffic source, up to 128 characters. Kept with the call; never shown to the agent. Also read as: subid, sub1, source_id. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": false,
          "description": "Send the parameters in the query string, as a form, or as JSON. Names aren't case-sensitive. When a name is in both the query string and the body, the query string wins. The query string may carry everything instead.",
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PingRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PingRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A bid, or a no-bid. Always HTTP 200.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PingAnswer"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/rtb/call-id": {
      "get": {
        "operationId": "attachCallIdGet",
        "tags": [
          "Real-time bidding"
        ],
        "summary": "Attach your call ID to a call",
        "description": "Your platform's own ID for a call. We send it back as {CALL_ID} in your postback, so your platform knows which call paid. Some platforms create their call ID only when the call reaches them. Send it to us then. Every answer is HTTP 200. Docs: https://app.inboundx.io/docs/call-id.",
        "security": [
          {
            "pingKey": []
          }
        ],
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": true,
            "description": "Your ping key for this campaign.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "caller_number",
            "in": "query",
            "required": true,
            "description": "The caller's phone number, ideally with the country code. We match the call to its bid by this number when it arrives. Also read as: caller_id, callerid, from, ani. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "request_id",
            "in": "query",
            "required": true,
            "description": "Your own ID for this call. We send it back in your postback as {CALL_ID}, and a repeat ping with the same ID gets the same bid back. Also read as: ringba-requestid, ringba_requestid, call_id, callid, sourcecallid, source_call_id, trace, external_id, clickid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "publisher_id",
            "in": "query",
            "required": false,
            "description": "Your publisher ID. When you send it, it must match the key. Also read as: publisher, afid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Attached, no match, or what was wrong with the request. Always HTTP 200.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallIdAnswer"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "attachCallIdPost",
        "tags": [
          "Real-time bidding"
        ],
        "summary": "Attach your call ID to a call",
        "description": "Your platform's own ID for a call. We send it back as {CALL_ID} in your postback, so your platform knows which call paid. Some platforms create their call ID only when the call reaches them. Send it to us then. Every answer is HTTP 200. Docs: https://app.inboundx.io/docs/call-id.",
        "security": [
          {
            "pingKey": []
          }
        ],
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": false,
            "description": "Your ping key for this campaign.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "caller_number",
            "in": "query",
            "required": false,
            "description": "The caller's phone number, ideally with the country code. We match the call to its bid by this number when it arrives. Also read as: caller_id, callerid, from, ani. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "request_id",
            "in": "query",
            "required": false,
            "description": "Your own ID for this call. We send it back in your postback as {CALL_ID}, and a repeat ping with the same ID gets the same bid back. Also read as: ringba-requestid, ringba_requestid, call_id, callid, sourcecallid, source_call_id, trace, external_id, clickid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "publisher_id",
            "in": "query",
            "required": false,
            "description": "Your publisher ID. When you send it, it must match the key. Also read as: publisher, afid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": false,
          "description": "Send the parameters in the query string, as a form, or as JSON. Names aren't case-sensitive. When a name is in both the query string and the body, the query string wins. The query string may carry everything instead.",
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/CallIdRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CallIdRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Attached, no match, or what was wrong with the request. Always HTTP 200.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CallIdAnswer"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/rtb/inspect": {
      "get": {
        "operationId": "inspectPingGet",
        "tags": [
          "Real-time bidding"
        ],
        "summary": "Inspect a ping without sending one",
        "description": "Enter your key and a test caller. The inspector reads your request exactly as a ping would and tells you what it found. It never holds an agent and never counts as traffic. It takes the ping's parameters. Docs: https://app.inboundx.io/docs/testing.",
        "security": [
          {
            "pingKey": []
          }
        ],
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": true,
            "description": "Your ping key for this campaign.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "publisher_id",
            "in": "query",
            "required": false,
            "description": "Your publisher ID. When you send it, it must match the key. Also read as: publisher, afid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "caller_number",
            "in": "query",
            "required": false,
            "description": "The caller's phone number, ideally with the country code. We match the call to its bid by this number when it arrives. Also read as: caller_id, callerid, from, ani. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "caller_state",
            "in": "query",
            "required": false,
            "description": "The caller's US state: its two-letter code or its full name (\"TX\" or \"Texas\"). If you leave it out, or we can't read it, we work it out from the zip, then from the area code. Also read as: state. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 32
            }
          },
          {
            "name": "request_id",
            "in": "query",
            "required": false,
            "description": "Your own ID for this call. We send it back in your postback as {CALL_ID}, and a repeat ping with the same ID gets the same bid back. Also read as: ringba-requestid, ringba_requestid, call_id, callid, sourcecallid, source_call_id, trace, external_id, clickid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "caller_zip",
            "in": "query",
            "required": false,
            "description": "The caller's five-digit ZIP code (ZIP+4 is fine; we keep the first five). If you don't send a state we can read, the zip decides it before the area code does. Also read as: zip, zipcode, zip_code, postal_code, inboundzipcode. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 16
            }
          },
          {
            "name": "caller_city",
            "in": "query",
            "required": false,
            "description": "The caller's city, shown on the call. Letters, spaces and .'- only, up to 64 characters. Also read as: city. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "caller_npa_nxx",
            "in": "query",
            "required": false,
            "description": "The first three or six digits of the caller's number. Shown for context only; a call is never matched by it. Also read as: npanxx, caller_prefix. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "age",
            "in": "query",
            "required": false,
            "description": "The caller's age, a whole number from 18 to 120. Also read as: caller_age. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "dob",
            "in": "query",
            "required": false,
            "description": "The caller's date of birth, as year-month-day or month/day/year. The agent sees the age, not the date. Also read as: date_of_birth. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "homeowner",
            "in": "query",
            "required": false,
            "description": "Whether the caller owns their home: yes or no. Also read as: home_owner, owns_home. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "insured",
            "in": "query",
            "required": false,
            "description": "Whether the caller has insurance now: yes or no. Also read as: currently_insured, has_insurance. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "current_carrier",
            "in": "query",
            "required": false,
            "description": "The caller's current insurer, up to 64 characters. Also read as: current_insurer. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "debt_amount",
            "in": "query",
            "required": false,
            "description": "The caller's debt, in whole dollars. A dollar sign and thousands separators are fine. Also read as: unsecured_debt, debt. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "destination_zip",
            "in": "query",
            "required": false,
            "description": "Where the caller is moving to: a five-digit ZIP code. Also read as: to_zip, move_to_zip. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "loan_amount",
            "in": "query",
            "required": false,
            "description": "The loan or mortgage balance, in whole dollars. Also read as: mortgage_balance. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "accident_date",
            "in": "query",
            "required": false,
            "description": "The date of the accident, as year-month-day or month/day/year. Also read as: incident_date. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "has_attorney",
            "in": "query",
            "required": false,
            "description": "Whether the caller already has an attorney: yes or no. Also read as: attorney, represented. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "trusted_form_cert_url",
            "in": "query",
            "required": false,
            "description": "The address of the caller's consent certificate, on https. Kept with the call for the buyer; never shown to the agent. Also read as: trustedform_cert_url, trusted_form_cert, trusted_form, trustedform, xxtrustedformcerturl. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "jornaya_leadid",
            "in": "query",
            "required": false,
            "description": "Your lead's universal ID. Kept with the call; never shown to the agent. Also read as: jornaya_lead_id, jornayaleadid, jleadid, leadid_token, universal_leadid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "sub_id",
            "in": "query",
            "required": false,
            "description": "Your own ID for the traffic source, up to 128 characters. Kept with the call; never shown to the agent. Also read as: subid, sub1, source_id. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "What a real ping with this request would have found. Always HTTP 200.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Inspection"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "inspectPingPost",
        "tags": [
          "Real-time bidding"
        ],
        "summary": "Inspect a ping without sending one",
        "description": "Enter your key and a test caller. The inspector reads your request exactly as a ping would and tells you what it found. It never holds an agent and never counts as traffic. It takes the ping's parameters. Docs: https://app.inboundx.io/docs/testing.",
        "security": [
          {
            "pingKey": []
          }
        ],
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": false,
            "description": "Your ping key for this campaign.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "publisher_id",
            "in": "query",
            "required": false,
            "description": "Your publisher ID. When you send it, it must match the key. Also read as: publisher, afid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "caller_number",
            "in": "query",
            "required": false,
            "description": "The caller's phone number, ideally with the country code. We match the call to its bid by this number when it arrives. Also read as: caller_id, callerid, from, ani. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "caller_state",
            "in": "query",
            "required": false,
            "description": "The caller's US state: its two-letter code or its full name (\"TX\" or \"Texas\"). If you leave it out, or we can't read it, we work it out from the zip, then from the area code. Also read as: state. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 32
            }
          },
          {
            "name": "request_id",
            "in": "query",
            "required": false,
            "description": "Your own ID for this call. We send it back in your postback as {CALL_ID}, and a repeat ping with the same ID gets the same bid back. Also read as: ringba-requestid, ringba_requestid, call_id, callid, sourcecallid, source_call_id, trace, external_id, clickid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "caller_zip",
            "in": "query",
            "required": false,
            "description": "The caller's five-digit ZIP code (ZIP+4 is fine; we keep the first five). If you don't send a state we can read, the zip decides it before the area code does. Also read as: zip, zipcode, zip_code, postal_code, inboundzipcode. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 16
            }
          },
          {
            "name": "caller_city",
            "in": "query",
            "required": false,
            "description": "The caller's city, shown on the call. Letters, spaces and .'- only, up to 64 characters. Also read as: city. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "name": "caller_npa_nxx",
            "in": "query",
            "required": false,
            "description": "The first three or six digits of the caller's number. Shown for context only; a call is never matched by it. Also read as: npanxx, caller_prefix. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "age",
            "in": "query",
            "required": false,
            "description": "The caller's age, a whole number from 18 to 120. Also read as: caller_age. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "dob",
            "in": "query",
            "required": false,
            "description": "The caller's date of birth, as year-month-day or month/day/year. The agent sees the age, not the date. Also read as: date_of_birth. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "homeowner",
            "in": "query",
            "required": false,
            "description": "Whether the caller owns their home: yes or no. Also read as: home_owner, owns_home. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "insured",
            "in": "query",
            "required": false,
            "description": "Whether the caller has insurance now: yes or no. Also read as: currently_insured, has_insurance. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "current_carrier",
            "in": "query",
            "required": false,
            "description": "The caller's current insurer, up to 64 characters. Also read as: current_insurer. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "debt_amount",
            "in": "query",
            "required": false,
            "description": "The caller's debt, in whole dollars. A dollar sign and thousands separators are fine. Also read as: unsecured_debt, debt. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "destination_zip",
            "in": "query",
            "required": false,
            "description": "Where the caller is moving to: a five-digit ZIP code. Also read as: to_zip, move_to_zip. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "loan_amount",
            "in": "query",
            "required": false,
            "description": "The loan or mortgage balance, in whole dollars. Also read as: mortgage_balance. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "accident_date",
            "in": "query",
            "required": false,
            "description": "The date of the accident, as year-month-day or month/day/year. Also read as: incident_date. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "has_attorney",
            "in": "query",
            "required": false,
            "description": "Whether the caller already has an attorney: yes or no. Also read as: attorney, represented. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "trusted_form_cert_url",
            "in": "query",
            "required": false,
            "description": "The address of the caller's consent certificate, on https. Kept with the call for the buyer; never shown to the agent. Also read as: trustedform_cert_url, trusted_form_cert, trusted_form, trustedform, xxtrustedformcerturl. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "jornaya_leadid",
            "in": "query",
            "required": false,
            "description": "Your lead's universal ID. Kept with the call; never shown to the agent. Also read as: jornaya_lead_id, jornayaleadid, jleadid, leadid_token, universal_leadid. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "sub_id",
            "in": "query",
            "required": false,
            "description": "Your own ID for the traffic source, up to 128 characters. Kept with the call; never shown to the agent. Also read as: subid, sub1, source_id. Names are not case-sensitive.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": false,
          "description": "Send the parameters in the query string, as a form, or as JSON. Names aren't case-sensitive. When a name is in both the query string and the body, the query string wins. The query string may carry everything instead.",
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/InspectRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InspectRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What a real ping with this request would have found. Always HTTP 200.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Inspection"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/mcp": {
      "post": {
        "operationId": "mcpJsonRpc",
        "tags": [
          "MCP"
        ],
        "summary": "Call the MCP server",
        "description": "One JSON-RPC 2.0 message per request (initialize, tools/list, tools/call). The tools are those of the signed-in publisher company, listed at https://app.inboundx.io/.well-known/mcp. Docs: https://app.inboundx.io/docs/mcp.",
        "security": [
          {
            "mcpOAuth": [
              "ix:owner"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The JSON-RPC result or error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "202": {
            "description": "A notification was accepted; there is no body."
          },
          "401": {
            "description": "No valid bearer token. The WWW-Authenticate header names the protected resource metadata to start OAuth from.",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                },
                "description": "Bearer, with resource_metadata and scope."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpUnauthorized"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "mcpGet",
        "tags": [
          "MCP"
        ],
        "summary": "Not used: the server speaks JSON-RPC over POST",
        "description": "There is no SSE stream. With a valid bearer token it answers 405; with none, or a token that is no longer valid, 401 naming the OAuth metadata, so a client can discover how to sign in.",
        "security": [],
        "responses": {
          "401": {
            "description": "No valid bearer token. The WWW-Authenticate header names the protected resource metadata to start OAuth from.",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                },
                "description": "Bearer, with resource_metadata and scope."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpUnauthorized"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/workspace/v1/calls": {
      "get": {
        "operationId": "listWorkspaceCalls",
        "tags": [
          "Workspace API"
        ],
        "summary": "List your workspace's calls",
        "description": "Calls, newest first: when, status, campaign, length, whether billable, whether an agent answered, the outcome, the caller's state and whether it sold. Docs: https://app.inboundx.io/docs/workspaces/api.",
        "security": [
          {
            "workspaceKey": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Rows per page, 1 to 500. Default 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The next_cursor of the previous page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Only rows made at or after this time (ISO 8601).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Only rows made before this time (ISO 8601).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "description": "Only this campaign.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The rows, or the totals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceCallList"
                }
              }
            }
          },
          "400": {
            "description": "invalid: A value in the request is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "missing_key: No API key was sent. invalid_key: The API key is not one we know. key_revoked: This API key has been revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "key_owner_inactive: The person who made this key can no longer use it. wrong_workspace: This key belongs to another workspace. insufficient_access: The person who made this key may not read this. workspace_inactive: This workspace still has to activate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Too many requests.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "internal_error: Something went wrong on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "servers": [
        {
          "url": "https://{workspace_host}",
          "description": "Your workspace's own address: the one your console is on.",
          "variables": {
            "workspace_host": {
              "default": "app.inboundx.io",
              "description": "Your workspace's host name."
            }
          }
        }
      ]
    },
    "/api/workspace/v1/campaigns": {
      "get": {
        "operationId": "listWorkspaceCampaigns",
        "tags": [
          "Workspace API"
        ],
        "summary": "List your workspace's campaigns",
        "description": "Campaigns, newest first: name, on or off, test or real, and industry. Docs: https://app.inboundx.io/docs/workspaces/api.",
        "security": [
          {
            "workspaceKey": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Rows per page, 1 to 500. Default 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The next_cursor of the previous page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The rows, or the totals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceCampaignList"
                }
              }
            }
          },
          "400": {
            "description": "invalid: A value in the request is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "missing_key: No API key was sent. invalid_key: The API key is not one we know. key_revoked: This API key has been revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "key_owner_inactive: The person who made this key can no longer use it. wrong_workspace: This key belongs to another workspace. insufficient_access: The person who made this key may not read this. workspace_inactive: This workspace still has to activate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Too many requests.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "internal_error: Something went wrong on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "servers": [
        {
          "url": "https://{workspace_host}",
          "description": "Your workspace's own address: the one your console is on.",
          "variables": {
            "workspace_host": {
              "default": "app.inboundx.io",
              "description": "Your workspace's host name."
            }
          }
        }
      ]
    },
    "/api/workspace/v1/stats": {
      "get": {
        "operationId": "getWorkspaceStats",
        "tags": [
          "Workspace API"
        ],
        "summary": "Totals for a window",
        "description": "Calls, answered, billable and sales for a window: the last 7 days by default, at most 92 days. Docs: https://app.inboundx.io/docs/workspaces/api.",
        "security": [
          {
            "workspaceKey": []
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Only rows made at or after this time (ISO 8601).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Only rows made before this time (ISO 8601).",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "description": "Only this campaign.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The rows, or the totals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceStatsAnswer"
                }
              }
            }
          },
          "400": {
            "description": "invalid: A value in the request is not valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "missing_key: No API key was sent. invalid_key: The API key is not one we know. key_revoked: This API key has been revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "key_owner_inactive: The person who made this key can no longer use it. wrong_workspace: This key belongs to another workspace. insufficient_access: The person who made this key may not read this. workspace_inactive: This workspace still has to activate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Too many requests.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "internal_error: Something went wrong on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "servers": [
        {
          "url": "https://{workspace_host}",
          "description": "Your workspace's own address: the one your console is on.",
          "variables": {
            "workspace_host": {
              "default": "app.inboundx.io",
              "description": "Your workspace's host name."
            }
          }
        }
      ]
    },
    "/api/workspace/v1/tools": {
      "get": {
        "operationId": "listWorkspaceTools",
        "tags": [
          "Workspace API"
        ],
        "summary": "The tools this key can call",
        "description": "Every AI connector tool this key may call right now: its name, scope (read or write), whether it moves money, what it does and its input schema. Docs: https://app.inboundx.io/docs/workspaces/api.",
        "security": [
          {
            "workspaceKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "The tools.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceToolList"
                }
              }
            }
          },
          "401": {
            "description": "missing_key: No API key was sent. invalid_key: The API key is not one we know. key_revoked: This API key has been revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "key_owner_inactive: The person who made this key can no longer use it. wrong_workspace: This key belongs to another workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Too many requests.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "internal_error: Something went wrong on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "servers": [
        {
          "url": "https://{workspace_host}",
          "description": "Your workspace's own address: the one your console is on.",
          "variables": {
            "workspace_host": {
              "default": "app.inboundx.io",
              "description": "Your workspace's host name."
            }
          }
        }
      ]
    },
    "/api/workspace/v1/tools/{tool}": {
      "post": {
        "operationId": "callWorkspaceTool",
        "tags": [
          "Workspace API"
        ],
        "summary": "Call one tool",
        "description": "Runs one AI connector tool with the JSON body as its input, with the rights of the key's maker. Read tools need a read or read_write key: workspace_overview, list_campaigns, get_campaign, list_publishers, get_publisher, list_agents, get_agent, list_calls, get_call, search_calls, get_transcript, list_rtb_pings, list_disputes, get_dispute, list_wallets, finance_summary, billing_and_plan, list_team, branding_and_domains, list_audit_log, diagnose_agent, diagnose_campaign, diagnose_publisher, team_status, search_help, request_feature, get_call_feedback, search_publisher_directory, list_publisher_requests, list_training, list_scripts, list_support_tickets, get_support_ticket, list_support_personas, list_feature_requests, list_api_keys, list_webhooks. Write tools need a write or read_write key: leave_call_feedback, pause_campaign, resume_campaign, approve_agent, suspend_agent, set_workspace_hours, set_campaign_states, add_publisher, set_publisher_settings, remove_publisher, restore_publisher, set_publisher_offer_status, assign_publisher_number, release_publisher_number, set_publisher_postback_url, send_publisher_invitation, revoke_publisher_setup_links, request_publisher_connection, decide_publisher_request, set_training_settings, save_training_module, move_training_module, set_training_quiz, set_course_module, save_script, set_script_active, reply_support_ticket, start_support_ticket, set_support_ticket_status, assign_support_ticket, add_support_ticket_note, save_support_saved_reply, add_support_persona, set_support_persona, set_campaign_coverage_mode, set_campaign_zips, remove_campaign, restore_campaign, set_campaign_notes, set_campaign_opening_script, block_signup_emails, unblock_signup_email, set_feature_request, update_workspace_general, set_maintenance_banner, set_maintenance_mode, set_live_campaign_limit, set_agent_onboarding_call, set_support_alerts, set_leaderboard_switch, set_agency_leaderboard_mode, save_brand_details, save_brand_theme, set_brand_default_theme, save_brand_options, add_custom_domain, recheck_custom_domain, disable_custom_domain, enable_custom_domain, delete_custom_domain, add_email_domain, recheck_email_domain, remove_email_domain, set_email_sender, save_kb_section, disable_kb_section, review_kb_section, create_vocabulary_set, update_vocabulary_set, copy_vocabulary_set, delete_vocabulary_set, set_default_vocabulary_set, save_sop_sheet, reset_sop_sheet, set_compliance_enabled, set_campaign_sheet_mode, save_campaign_sheet, revoke_api_key, set_webhook_enabled, delete_webhook, propose_wallet_credit, propose_force_bill, propose_dispute_decision, preview_invite_agents, confirm_invite_agents, invite_manager, set_campaign_roster, set_campaign_availability, reject_agent, unsuspend_agent, review_kyc, set_manager_page_access, set_manager_mcp_access, set_coowner, set_agent_referral_member, retire_agent_referral_code. Without an Idempotency-Key most writes answer with a preview and a confirm_token; send the same body with confirm true and that token to apply it. leave_call_feedback has no preview: it saves in the first request. With an Idempotency-Key the write is applied in this request, and a retry with the same key within 24 hours returns the first answer. A tool that moves money needs the Idempotency-Key and a key the owner made. Docs: https://app.inboundx.io/docs/workspaces/api.",
        "security": [
          {
            "workspaceKey": []
          }
        ],
        "parameters": [
          {
            "name": "tool",
            "in": "path",
            "required": true,
            "description": "The tool's name.",
            "schema": {
              "type": "string",
              "enum": [
                "workspace_overview",
                "list_campaigns",
                "get_campaign",
                "list_publishers",
                "get_publisher",
                "list_agents",
                "get_agent",
                "list_calls",
                "get_call",
                "search_calls",
                "get_transcript",
                "list_rtb_pings",
                "list_disputes",
                "get_dispute",
                "list_wallets",
                "finance_summary",
                "billing_and_plan",
                "list_team",
                "branding_and_domains",
                "list_audit_log",
                "diagnose_agent",
                "diagnose_campaign",
                "diagnose_publisher",
                "team_status",
                "search_help",
                "request_feature",
                "leave_call_feedback",
                "get_call_feedback",
                "pause_campaign",
                "resume_campaign",
                "approve_agent",
                "suspend_agent",
                "set_workspace_hours",
                "set_campaign_states",
                "add_publisher",
                "set_publisher_settings",
                "remove_publisher",
                "restore_publisher",
                "set_publisher_offer_status",
                "assign_publisher_number",
                "release_publisher_number",
                "set_publisher_postback_url",
                "send_publisher_invitation",
                "revoke_publisher_setup_links",
                "search_publisher_directory",
                "list_publisher_requests",
                "request_publisher_connection",
                "decide_publisher_request",
                "list_training",
                "set_training_settings",
                "save_training_module",
                "move_training_module",
                "set_training_quiz",
                "set_course_module",
                "list_scripts",
                "save_script",
                "set_script_active",
                "list_support_tickets",
                "get_support_ticket",
                "list_support_personas",
                "reply_support_ticket",
                "start_support_ticket",
                "set_support_ticket_status",
                "assign_support_ticket",
                "add_support_ticket_note",
                "save_support_saved_reply",
                "add_support_persona",
                "set_support_persona",
                "set_campaign_coverage_mode",
                "set_campaign_zips",
                "remove_campaign",
                "restore_campaign",
                "set_campaign_notes",
                "set_campaign_opening_script",
                "block_signup_emails",
                "unblock_signup_email",
                "list_feature_requests",
                "set_feature_request",
                "update_workspace_general",
                "set_maintenance_banner",
                "set_maintenance_mode",
                "set_live_campaign_limit",
                "set_agent_onboarding_call",
                "set_support_alerts",
                "set_leaderboard_switch",
                "set_agency_leaderboard_mode",
                "save_brand_details",
                "save_brand_theme",
                "set_brand_default_theme",
                "save_brand_options",
                "add_custom_domain",
                "recheck_custom_domain",
                "disable_custom_domain",
                "enable_custom_domain",
                "delete_custom_domain",
                "add_email_domain",
                "recheck_email_domain",
                "remove_email_domain",
                "set_email_sender",
                "save_kb_section",
                "disable_kb_section",
                "review_kb_section",
                "create_vocabulary_set",
                "update_vocabulary_set",
                "copy_vocabulary_set",
                "delete_vocabulary_set",
                "set_default_vocabulary_set",
                "save_sop_sheet",
                "reset_sop_sheet",
                "set_compliance_enabled",
                "set_campaign_sheet_mode",
                "save_campaign_sheet",
                "list_api_keys",
                "revoke_api_key",
                "list_webhooks",
                "set_webhook_enabled",
                "delete_webhook",
                "propose_wallet_credit",
                "propose_force_bill",
                "propose_dispute_decision",
                "preview_invite_agents",
                "confirm_invite_agents",
                "invite_manager",
                "set_campaign_roster",
                "set_campaign_availability",
                "reject_agent",
                "unsuspend_agent",
                "review_kyc",
                "set_manager_page_access",
                "set_manager_mcp_access",
                "set_coowner",
                "set_agent_referral_member",
                "retire_agent_referral_code"
              ]
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "1 to 255 visible ASCII characters, unique per change. Applies a write in one request and makes retries safe.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The tool's input, as listed by the tools list.",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The tool's answer. A replayed answer carries Idempotent-Replayed: true.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceToolAnswer"
                }
              }
            },
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                "description": "Present when this is the stored answer to an earlier request with the same Idempotency-Key."
              }
            }
          },
          "400": {
            "description": "invalid: A value in the request is not valid. idempotency_required: This tool moves money and needs an Idempotency-Key header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "missing_key: No API key was sent. invalid_key: The API key is not one we know. key_revoked: This API key has been revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "key_owner_inactive: The person who made this key can no longer use it. wrong_workspace: This key belongs to another workspace. insufficient_access: The person who made this key may not read this. workspace_inactive: This workspace still has to activate. scope: This key's scope does not allow this. owner_only: Only a key made by the workspace owner can move money.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "unknown_tool: There is no tool with that name. not_found: That record was not found in this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "405": {
            "description": "The method is not one this path answers.",
            "headers": {
              "Allow": {
                "schema": {
                  "type": "string"
                },
                "description": "The methods this path answers."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "409": {
            "description": "changed: Something changed while the request was running. Nothing was applied. idempotency_in_progress: A request with this Idempotency-Key is still running, or ended without an answer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "422": {
            "description": "refused: The change was refused. idempotency_conflict: This Idempotency-Key was already used for a different request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited: Too many requests.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "internal_error: Something went wrong on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "servers": [
        {
          "url": "https://{workspace_host}",
          "description": "Your workspace's own address: the one your console is on.",
          "variables": {
            "workspace_host": {
              "default": "app.inboundx.io",
              "description": "Your workspace's host name."
            }
          }
        }
      ]
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApi",
        "tags": [
          "Discovery"
        ],
        "summary": "This document",
        "description": "The OpenAPI 3.1 description of the public API.",
        "security": [],
        "responses": {
          "200": {
            "description": "This document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "This host does not serve the public API description.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/openapi.json": {
      "get": {
        "operationId": "getOpenApiAlias",
        "tags": [
          "Discovery"
        ],
        "summary": "This document (API alias)",
        "description": "The OpenAPI 3.1 description of the public API.",
        "security": [],
        "responses": {
          "200": {
            "description": "This document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "This host does not serve the public API description.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "signedDelivery": {
      "post": {
        "operationId": "receiveSignedDelivery",
        "summary": "A signed webhook event we send you",
        "security": [],
        "description": "Verify each event, process it once, and acknowledge it after your work is saved. Configure a public HTTPS destination in your webhook settings. Choose GET, POST or PUT and the events you need. Save the signing secret shown when you create or rotate the webhook: it is shown only once. Use that exact string as a UTF-8 HMAC key; do not decode it from base64. The signed message contains five values separated by one LF byte (a line break), with no trailing newline. The method is uppercase. The message is: \"delivery_id\\ntimestamp\\nUPPERCASE_METHOD\\npublic_url\\nraw_body\". Delivery is at least once. Your server can save its work and lose the HTTP response; a retry may repeat an event you already processed. Redeliver also keeps the original delivery ID. The default is five total attempts. The first retry waits 30 seconds; later delays double, up to one hour. Your webhook setting can change the attempt budget. Each HTTP attempt has a five-second deadline. Docs: https://app.inboundx.io/docs/webhooks.",
        "parameters": [
          {
            "name": "x-ix-delivery-id",
            "in": "header",
            "required": true,
            "description": "A stable UUID for this delivery, including retries and Redeliver.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-ix-event",
            "in": "header",
            "required": true,
            "description": "The subscribed event name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-ix-timestamp",
            "in": "header",
            "required": true,
            "description": "Unix time in whole seconds, refreshed for each attempt.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-ix-signature",
            "in": "header",
            "required": true,
            "description": "sha256= followed by the lowercase hexadecimal HMAC-SHA256 digest.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "The event, as configured on the webhook.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return 2xx after committing to acknowledge delivery, including a duplicate. Return 5xx, 408 or 429 for a temporary failure. Redirects and other 4xx responses are final failures; redirects are not followed."
          }
        }
      }
    },
    "workspaceCallEvent": {
      "post": {
        "operationId": "receiveWorkspaceCallEvent",
        "summary": "A call event we send to a workspace's own webhook",
        "security": [],
        "description": "An owner adds an https endpoint in Settings > API and webhooks and picks events: call.completed, call.converted. X-Webhook-Signature is t=TIMESTAMP,v1=HEX: TIMESTAMP is unix seconds and HEX is the HMAC-SHA256, with the endpoint's secret, of the timestamp, a dot and the raw body. Refuse a timestamp more than 300 seconds off. Answer 2xx within 10 seconds. Anything else is retried after 1, 5, 30, 120, 360 minutes, then marked failed. The body's id stays the same on every retry, so drop repeats by id. The call carries no money amounts, no traffic source and no agent.",
        "parameters": [
          {
            "name": "X-Webhook-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Webhook-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Webhook-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Webhook-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "event",
                  "created_at",
                  "data"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "event": {
                    "type": "string",
                    "enum": [
                      "call.completed",
                      "call.converted",
                      "webhook.test"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "call": {
                        "$ref": "#/components/schemas/WorkspaceCall"
                      },
                      "sold_at": {
                        "type": "string",
                        "format": "date-time",
                        "description": "call.converted only."
                      },
                      "test": {
                        "type": "boolean",
                        "description": "webhook.test only: always true."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx marks the delivery delivered."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "publisherKey": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "The existing publisher portal API uses Authorization: Bearer <key>, with an ixa_ key created in API keys. Ping keys are separate and travel as parameters."
      },
      "workspaceKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "A workspace API key (ixw_...), made by an owner in Settings > API and webhooks and shown once. Its scope is read, write or read_write. It reaches only its own workspace, with the rights of the person who made it, up to 60 requests a minute."
      },
      "pingKey": {
        "type": "apiKey",
        "in": "query",
        "name": "key",
        "description": "Your ping key for this campaign. Send it as the key parameter, in the query string or the body."
      },
      "mcpOAuth": {
        "type": "oauth2",
        "description": "OAuth 2.1 with PKCE (S256) and dynamic client registration at https://app.inboundx.io/api/mcp/oauth/register. Metadata: https://app.inboundx.io/.well-known/oauth-authorization-server and https://app.inboundx.io/.well-known/oauth-protected-resource.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://app.inboundx.io/mcp/authorize",
            "tokenUrl": "https://app.inboundx.io/api/mcp/oauth/token",
            "refreshUrl": "https://app.inboundx.io/api/mcp/oauth/token",
            "scopes": {
              "ix:owner": "Act for the signed-in account through the InboundX MCP server, with that account's own rights."
            }
          }
        }
      }
    },
    "schemas": {
      "PingRequest": {
        "type": "object",
        "required": [
          "key",
          "caller_number"
        ],
        "properties": {
          "key": {
            "type": "string",
            "maxLength": 128,
            "description": "Your ping key for this campaign."
          },
          "publisher_id": {
            "type": "string",
            "maxLength": 128,
            "description": "Your publisher ID. When you send it, it must match the key. Also read as: publisher, afid. Names are not case-sensitive."
          },
          "caller_number": {
            "type": "string",
            "maxLength": 64,
            "description": "The caller's phone number, ideally with the country code. We match the call to its bid by this number when it arrives. Also read as: caller_id, callerid, from, ani. Names are not case-sensitive."
          },
          "caller_state": {
            "type": "string",
            "maxLength": 32,
            "description": "The caller's US state: its two-letter code or its full name (\"TX\" or \"Texas\"). If you leave it out, or we can't read it, we work it out from the zip, then from the area code. Also read as: state. Names are not case-sensitive."
          },
          "request_id": {
            "type": "string",
            "maxLength": 512,
            "description": "Your own ID for this call. We send it back in your postback as {CALL_ID}, and a repeat ping with the same ID gets the same bid back. Also read as: ringba-requestid, ringba_requestid, call_id, callid, sourcecallid, source_call_id, trace, external_id, clickid. Names are not case-sensitive."
          },
          "caller_zip": {
            "type": "string",
            "maxLength": 16,
            "description": "The caller's five-digit ZIP code (ZIP+4 is fine; we keep the first five). If you don't send a state we can read, the zip decides it before the area code does. Also read as: zip, zipcode, zip_code, postal_code, inboundzipcode. Names are not case-sensitive."
          },
          "caller_city": {
            "type": "string",
            "maxLength": 64,
            "description": "The caller's city, shown on the call. Letters, spaces and .'- only, up to 64 characters. Also read as: city. Names are not case-sensitive."
          },
          "caller_npa_nxx": {
            "type": "string",
            "maxLength": 128,
            "description": "The first three or six digits of the caller's number. Shown for context only; a call is never matched by it. Also read as: npanxx, caller_prefix. Names are not case-sensitive."
          },
          "age": {
            "type": "string",
            "maxLength": 128,
            "description": "The caller's age, a whole number from 18 to 120. Also read as: caller_age. Names are not case-sensitive."
          },
          "dob": {
            "type": "string",
            "maxLength": 128,
            "description": "The caller's date of birth, as year-month-day or month/day/year. The agent sees the age, not the date. Also read as: date_of_birth. Names are not case-sensitive."
          },
          "homeowner": {
            "type": "string",
            "maxLength": 128,
            "description": "Whether the caller owns their home: yes or no. Also read as: home_owner, owns_home. Names are not case-sensitive."
          },
          "insured": {
            "type": "string",
            "maxLength": 128,
            "description": "Whether the caller has insurance now: yes or no. Also read as: currently_insured, has_insurance. Names are not case-sensitive."
          },
          "current_carrier": {
            "type": "string",
            "maxLength": 128,
            "description": "The caller's current insurer, up to 64 characters. Also read as: current_insurer. Names are not case-sensitive."
          },
          "debt_amount": {
            "type": "string",
            "maxLength": 128,
            "description": "The caller's debt, in whole dollars. A dollar sign and thousands separators are fine. Also read as: unsecured_debt, debt. Names are not case-sensitive."
          },
          "destination_zip": {
            "type": "string",
            "maxLength": 128,
            "description": "Where the caller is moving to: a five-digit ZIP code. Also read as: to_zip, move_to_zip. Names are not case-sensitive."
          },
          "loan_amount": {
            "type": "string",
            "maxLength": 128,
            "description": "The loan or mortgage balance, in whole dollars. Also read as: mortgage_balance. Names are not case-sensitive."
          },
          "accident_date": {
            "type": "string",
            "maxLength": 128,
            "description": "The date of the accident, as year-month-day or month/day/year. Also read as: incident_date. Names are not case-sensitive."
          },
          "has_attorney": {
            "type": "string",
            "maxLength": 128,
            "description": "Whether the caller already has an attorney: yes or no. Also read as: attorney, represented. Names are not case-sensitive."
          },
          "trusted_form_cert_url": {
            "type": "string",
            "maxLength": 128,
            "description": "The address of the caller's consent certificate, on https. Kept with the call for the buyer; never shown to the agent. Also read as: trustedform_cert_url, trusted_form_cert, trusted_form, trustedform, xxtrustedformcerturl. Names are not case-sensitive."
          },
          "jornaya_leadid": {
            "type": "string",
            "maxLength": 128,
            "description": "Your lead's universal ID. Kept with the call; never shown to the agent. Also read as: jornaya_lead_id, jornayaleadid, jleadid, leadid_token, universal_leadid. Names are not case-sensitive."
          },
          "sub_id": {
            "type": "string",
            "maxLength": 128,
            "description": "Your own ID for the traffic source, up to 128 characters. Kept with the call; never shown to the agent. Also read as: subid, sub1, source_id. Names are not case-sensitive."
          }
        },
        "additionalProperties": {
          "type": "string"
        }
      },
      "InspectRequest": {
        "type": "object",
        "required": [
          "key"
        ],
        "properties": {
          "key": {
            "type": "string",
            "maxLength": 128,
            "description": "Your ping key for this campaign."
          },
          "publisher_id": {
            "type": "string",
            "maxLength": 128,
            "description": "Your publisher ID. When you send it, it must match the key. Also read as: publisher, afid. Names are not case-sensitive."
          },
          "caller_number": {
            "type": "string",
            "maxLength": 64,
            "description": "The caller's phone number, ideally with the country code. We match the call to its bid by this number when it arrives. Also read as: caller_id, callerid, from, ani. Names are not case-sensitive."
          },
          "caller_state": {
            "type": "string",
            "maxLength": 32,
            "description": "The caller's US state: its two-letter code or its full name (\"TX\" or \"Texas\"). If you leave it out, or we can't read it, we work it out from the zip, then from the area code. Also read as: state. Names are not case-sensitive."
          },
          "request_id": {
            "type": "string",
            "maxLength": 512,
            "description": "Your own ID for this call. We send it back in your postback as {CALL_ID}, and a repeat ping with the same ID gets the same bid back. Also read as: ringba-requestid, ringba_requestid, call_id, callid, sourcecallid, source_call_id, trace, external_id, clickid. Names are not case-sensitive."
          },
          "caller_zip": {
            "type": "string",
            "maxLength": 16,
            "description": "The caller's five-digit ZIP code (ZIP+4 is fine; we keep the first five). If you don't send a state we can read, the zip decides it before the area code does. Also read as: zip, zipcode, zip_code, postal_code, inboundzipcode. Names are not case-sensitive."
          },
          "caller_city": {
            "type": "string",
            "maxLength": 64,
            "description": "The caller's city, shown on the call. Letters, spaces and .'- only, up to 64 characters. Also read as: city. Names are not case-sensitive."
          },
          "caller_npa_nxx": {
            "type": "string",
            "maxLength": 128,
            "description": "The first three or six digits of the caller's number. Shown for context only; a call is never matched by it. Also read as: npanxx, caller_prefix. Names are not case-sensitive."
          },
          "age": {
            "type": "string",
            "maxLength": 128,
            "description": "The caller's age, a whole number from 18 to 120. Also read as: caller_age. Names are not case-sensitive."
          },
          "dob": {
            "type": "string",
            "maxLength": 128,
            "description": "The caller's date of birth, as year-month-day or month/day/year. The agent sees the age, not the date. Also read as: date_of_birth. Names are not case-sensitive."
          },
          "homeowner": {
            "type": "string",
            "maxLength": 128,
            "description": "Whether the caller owns their home: yes or no. Also read as: home_owner, owns_home. Names are not case-sensitive."
          },
          "insured": {
            "type": "string",
            "maxLength": 128,
            "description": "Whether the caller has insurance now: yes or no. Also read as: currently_insured, has_insurance. Names are not case-sensitive."
          },
          "current_carrier": {
            "type": "string",
            "maxLength": 128,
            "description": "The caller's current insurer, up to 64 characters. Also read as: current_insurer. Names are not case-sensitive."
          },
          "debt_amount": {
            "type": "string",
            "maxLength": 128,
            "description": "The caller's debt, in whole dollars. A dollar sign and thousands separators are fine. Also read as: unsecured_debt, debt. Names are not case-sensitive."
          },
          "destination_zip": {
            "type": "string",
            "maxLength": 128,
            "description": "Where the caller is moving to: a five-digit ZIP code. Also read as: to_zip, move_to_zip. Names are not case-sensitive."
          },
          "loan_amount": {
            "type": "string",
            "maxLength": 128,
            "description": "The loan or mortgage balance, in whole dollars. Also read as: mortgage_balance. Names are not case-sensitive."
          },
          "accident_date": {
            "type": "string",
            "maxLength": 128,
            "description": "The date of the accident, as year-month-day or month/day/year. Also read as: incident_date. Names are not case-sensitive."
          },
          "has_attorney": {
            "type": "string",
            "maxLength": 128,
            "description": "Whether the caller already has an attorney: yes or no. Also read as: attorney, represented. Names are not case-sensitive."
          },
          "trusted_form_cert_url": {
            "type": "string",
            "maxLength": 128,
            "description": "The address of the caller's consent certificate, on https. Kept with the call for the buyer; never shown to the agent. Also read as: trustedform_cert_url, trusted_form_cert, trusted_form, trustedform, xxtrustedformcerturl. Names are not case-sensitive."
          },
          "jornaya_leadid": {
            "type": "string",
            "maxLength": 128,
            "description": "Your lead's universal ID. Kept with the call; never shown to the agent. Also read as: jornaya_lead_id, jornayaleadid, jleadid, leadid_token, universal_leadid. Names are not case-sensitive."
          },
          "sub_id": {
            "type": "string",
            "maxLength": 128,
            "description": "Your own ID for the traffic source, up to 128 characters. Kept with the call; never shown to the agent. Also read as: subid, sub1, source_id. Names are not case-sensitive."
          }
        },
        "additionalProperties": {
          "type": "string"
        }
      },
      "CallIdRequest": {
        "type": "object",
        "required": [
          "key",
          "caller_number",
          "request_id"
        ],
        "properties": {
          "key": {
            "type": "string",
            "maxLength": 128,
            "description": "Your ping key for this campaign."
          },
          "caller_number": {
            "type": "string",
            "maxLength": 64,
            "description": "The caller's phone number, ideally with the country code. We match the call to its bid by this number when it arrives. Also read as: caller_id, callerid, from, ani. Names are not case-sensitive."
          },
          "request_id": {
            "type": "string",
            "maxLength": 512,
            "description": "Your own ID for this call. We send it back in your postback as {CALL_ID}, and a repeat ping with the same ID gets the same bid back. Also read as: ringba-requestid, ringba_requestid, call_id, callid, sourcecallid, source_call_id, trace, external_id, clickid. Names are not case-sensitive."
          },
          "publisher_id": {
            "type": "string",
            "maxLength": 128,
            "description": "Your publisher ID. When you send it, it must match the key. Also read as: publisher, afid. Names are not case-sensitive."
          }
        },
        "additionalProperties": {
          "type": "string"
        }
      },
      "Bid": {
        "type": "object",
        "description": "Repeat pings for the same caller and campaign reuse the active bid. Holds default to 15 seconds; a campaign may use 10–30 seconds. Always dial before expires_at. During a database interruption, a clean no-target response is returned within one second. Optional traffic shields may reduce bids from sources that send pings without calls.",
        "required": [
          "uuid",
          "status",
          "payout",
          "seconds",
          "sip_address",
          "expires_at",
          "caller_number"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid",
            "description": "The bid's ID."
          },
          "status": {
            "type": "string",
            "const": "reserved",
            "description": "Always reserved on a bid."
          },
          "retreaver_payout": {
            "type": "number",
            "minimum": 0,
            "description": "What the call pays, in US dollars. The same number as payout, present only for Retreaver or unknown/legacy connection platforms."
          },
          "retreaver_seconds": {
            "type": "integer",
            "minimum": 0,
            "description": "How long the call must last to pay, in seconds. The same number as seconds, present only for Retreaver or unknown/legacy connection platforms."
          },
          "payout": {
            "type": "number",
            "minimum": 0,
            "description": "What the call pays, in US dollars."
          },
          "seconds": {
            "type": "integer",
            "minimum": 0,
            "description": "How long the call must last to pay, in seconds."
          },
          "sip_address": {
            "type": "string",
            "examples": [
              "SIP_ADDRESS_FROM_THE_BID"
            ],
            "description": "Where to send the call. Dial it exactly as it is."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the hold ends. Send the call before then."
          },
          "caller_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "The caller's number as we read it. Compare it with yours to catch a formatting problem."
          }
        },
        "additionalProperties": false,
        "examples": [
          {
            "uuid": "3f1c2a4e-9b7d-4c1e-8a2f-5d6b7c8e9f01",
            "status": "reserved",
            "payout": 45,
            "seconds": 60,
            "sip_address": "SIP_ADDRESS_FROM_THE_BID",
            "expires_at": "2026-01-15T17:30:15.000Z",
            "caller_number": "+15555550123"
          }
        ]
      },
      "NoBid": {
        "type": "object",
        "description": "When we can't take the caller, the answer is still HTTP 200:",
        "required": [
          "status",
          "reason"
        ],
        "properties": {
          "status": {
            "type": "string",
            "const": "no-target"
          },
          "reason": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9_]{0,63}$",
            "description": "Why there is no bid. Every reason, and what to do about it: https://app.inboundx.io/docs/errors."
          }
        },
        "additionalProperties": false
      },
      "PingAnswer": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/Bid"
          },
          {
            "$ref": "#/components/schemas/NoBid"
          }
        ]
      },
      "CallIdAnswer": {
        "oneOf": [
          {
            "type": "object",
            "description": "Attached. The answer says whether we found the call itself or the bid still held for it:",
            "required": [
              "status",
              "matched",
              "via"
            ],
            "properties": {
              "status": {
                "type": "string",
                "const": "ok"
              },
              "matched": {
                "type": "boolean",
                "const": true
              },
              "via": {
                "type": "string",
                "enum": [
                  "call",
                  "reservation"
                ]
              }
            },
            "additionalProperties": false
          },
          {
            "type": "object",
            "description": "We found no recent call or held bid for that caller on this key. Check the number.",
            "required": [
              "status"
            ],
            "properties": {
              "status": {
                "type": "string",
                "const": "no_match"
              }
            },
            "additionalProperties": false
          },
          {
            "type": "object",
            "description": "The request itself was wrong. The answer says what:",
            "required": [
              "status",
              "reason"
            ],
            "properties": {
              "status": {
                "type": "string",
                "const": "error"
              },
              "reason": {
                "type": "string",
                "enum": [
                  "missing_key",
                  "missing_caller_number",
                  "missing_request_id",
                  "bad_caller_number",
                  "lookup_failed",
                  "unknown_key",
                  "publisher_mismatch"
                ]
              }
            },
            "additionalProperties": false
          }
        ]
      },
      "Inspection": {
        "type": "object",
        "description": "A dry-run report. The sections present depend on how far the request got: no key stops after auth.",
        "required": [
          "status",
          "note",
          "received",
          "auth"
        ],
        "properties": {
          "status": {
            "type": "string",
            "const": "inspection"
          },
          "note": {
            "type": "string"
          },
          "received": {
            "type": "object",
            "properties": {
              "method": {
                "type": "string"
              },
              "params": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              }
            }
          },
          "auth": {
            "type": "object",
            "properties": {
              "key": {
                "type": "string"
              }
            },
            "additionalProperties": true
          },
          "resolved": {
            "type": "object",
            "additionalProperties": true
          },
          "offer": {
            "type": "object",
            "additionalProperties": true
          },
          "lead_fields": {
            "type": "object",
            "additionalProperties": true
          },
          "campaign": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": true
              },
              {
                "type": "string"
              }
            ]
          },
          "auction_right_now": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ]
          },
          "method": {
            "type": "string",
            "examples": [
              "initialize",
              "tools/list",
              "tools/call"
            ]
          },
          "params": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": [
          "jsonrpc"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ]
          },
          "result": {
            "type": "object",
            "additionalProperties": true
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              },
              "data": {}
            }
          }
        }
      },
      "McpUnauthorized": {
        "type": "object",
        "required": [
          "error",
          "error_description",
          "message",
          "hint"
        ],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "invalid_token",
              "invalid_request"
            ],
            "description": "The RFC 6750 error code."
          },
          "error_description": {
            "type": "string"
          },
          "message": {
            "type": "string",
            "description": "What went wrong."
          },
          "hint": {
            "type": "string",
            "description": "What to do next."
          }
        }
      },
      "WorkspaceCall": {
        "type": "object",
        "required": [
          "id",
          "created_at",
          "ended_at",
          "status",
          "campaign_id",
          "duration_seconds",
          "billable",
          "billable_seconds",
          "disposition",
          "ended_reason",
          "caller_state",
          "answered",
          "sold"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "ended_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "status": {
            "type": "string"
          },
          "campaign_id": {
            "type": "string",
            "format": "uuid"
          },
          "duration_seconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "billable": {
            "type": "boolean"
          },
          "billable_seconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "disposition": {
            "type": [
              "string",
              "null"
            ],
            "description": "The outcome the agent chose."
          },
          "ended_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "caller_state": {
            "type": [
              "string",
              "null"
            ],
            "description": "The caller's US state, when known."
          },
          "answered": {
            "type": "boolean",
            "description": "Whether an agent took the call."
          },
          "sold": {
            "type": "boolean",
            "description": "Whether a sale was recorded on the call."
          }
        },
        "additionalProperties": false
      },
      "WorkspaceCampaign": {
        "type": "object",
        "required": [
          "id",
          "created_at",
          "name",
          "active",
          "is_test",
          "industry"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "name": {
            "type": "string"
          },
          "active": {
            "type": "boolean"
          },
          "is_test": {
            "type": "boolean"
          },
          "industry": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "WorkspaceCallList": {
        "type": "object",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkspaceCall"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as cursor for the next page. Null on the last page."
          }
        }
      },
      "WorkspaceCampaignList": {
        "type": "object",
        "required": [
          "data",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkspaceCampaign"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as cursor for the next page. Null on the last page."
          }
        }
      },
      "WorkspaceToolList": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "scope",
                "money",
                "description",
                "input_schema",
                "method",
                "path"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "scope": {
                  "type": "string",
                  "enum": [
                    "read",
                    "write"
                  ]
                },
                "money": {
                  "type": "boolean",
                  "description": "True when the tool moves money: it needs an Idempotency-Key and the owner's key."
                },
                "description": {
                  "type": "string"
                },
                "input_schema": {
                  "type": "object",
                  "description": "JSON Schema of the request body.",
                  "additionalProperties": true
                },
                "method": {
                  "type": "string",
                  "enum": [
                    "POST"
                  ]
                },
                "path": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "WorkspaceToolAnswer": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "description": "What the tool returned. A previewing write without an Idempotency-Key returns preview, summary, change and confirm_token; an applied write returns done and summary; nothing to change returns changed false."
          }
        }
      },
      "WorkspaceStatsAnswer": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "from",
              "to",
              "campaign_id",
              "calls",
              "answered",
              "billable",
              "sold"
            ],
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time"
              },
              "to": {
                "type": "string",
                "format": "date-time"
              },
              "campaign_id": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uuid"
              },
              "calls": {
                "type": "integer"
              },
              "answered": {
                "type": "integer"
              },
              "billable": {
                "type": "integer"
              },
              "sold": {
                "type": "integer",
                "description": "Sales recorded in the window."
              }
            },
            "additionalProperties": false
          }
        }
      },
      "ApiError": {
        "type": "object",
        "description": "The error every other InboundX API answer carries: a code to branch on, a message and a hint.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "hint"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "field": {
                "type": "string",
                "description": "The parameter that was not valid, when there is one."
              },
              "reason": {
                "type": "string",
                "description": "Why a change was refused, when one was."
              },
              "message": {
                "type": "string"
              },
              "hint": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}
