{
  "areas": {
    "conv": "Click IDs and conversions",
    "jobs": "Jobs",
    "raw": "Raw data"
  },
  "base": "/api/v1",
  "endpoints": [
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": true,
        "title": "Who am I"
      },
      "area": "",
      "command": "",
      "cost": "read",
      "example": {
        "access": {
          "conv": "read",
          "jobs": "do",
          "raw": "read"
        },
        "companies": [
          {
            "id": "cmp_14f5",
            "name": "Sunrise Moving",
            "time_zone": "America/Chicago"
          },
          {
            "id": "cmp_8a21",
            "name": "Capital Roofing",
            "time_zone": "America/New_York"
          }
        ],
        "credential": {
          "acts_as": "Ariel T.",
          "hide_personal_details": false,
          "kind": "key",
          "name": "Reporting laptop"
        },
        "job_types": [
          {
            "id": "jt_02",
            "name": "Local move"
          },
          {
            "id": "jt_05",
            "name": "Roof repair"
          }
        ],
        "statuses": [
          {
            "id": "st_01",
            "name": "Draft",
            "slug": "draft"
          },
          {
            "id": "st_02",
            "name": "Scheduled",
            "slug": "scheduled"
          },
          {
            "id": "st_03",
            "name": "Dispatched",
            "slug": "dispatched"
          },
          {
            "id": "st_06",
            "name": "Closed",
            "slug": "closed"
          }
        ],
        "technicians": [
          {
            "id": "wrk_3b90",
            "name": "Dana R.",
            "role": "technician"
          },
          {
            "id": "wrk_7a12",
            "name": "Omar S.",
            "role": "subcontractor"
          }
        ],
        "workspace": {
          "id": "org_2c7d",
          "name": "Northwind Ads"
        }
      },
      "exception": "",
      "group": "Account",
      "id": "whoami",
      "mcp": true,
      "mcp_why": "",
      "method": "GET",
      "need": "read",
      "notes": [
        [
          "Call this first",
          "Everything a command needs by id is here: the company, the status, the job type, the technician. A name works too where the docs say so, and the answer tells you which name it understood."
        ]
      ],
      "params": [],
      "path": "/api/v1/whoami",
      "returns": "The workspace, the credential, the companies it may name, its access per area, and the lookups a command needs: every status, job type and technician, with ids. Lookups are carried here so a client never has to guess a name.",
      "says": "Who this key or app acts as, which companies it reaches, what it may do, and the words to use in commands: statuses, job types and technicians.",
      "source": "read side: view_workspace_card, view_reference_job_status, view_reference_job_type, view_reference_worker",
      "title": "Who am I"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": true,
        "title": "List jobs"
      },
      "area": "jobs",
      "command": "",
      "cost": "read",
      "example": {
        "count": 128,
        "items": [
          {
            "address": {
              "city": "Bloomington",
              "line1": "2590 Main St",
              "line2": "",
              "one_line": "2590 Main St, Bloomington, IL 61701",
              "postal_code": "61701",
              "state": "IL"
            },
            "canceled_at": null,
            "client": {
              "id": "cli_77a0",
              "name": "R. Azoulay",
              "phone": "+12175550142",
              "second_phone": ""
            },
            "closed_at": null,
            "collected": 0.0,
            "company": {
              "id": "cmp_14f5",
              "name": "Sunrise Moving"
            },
            "created_at": "2026-10-11T18:25:40Z",
            "currency": "USD",
            "id": "job_1138",
            "is_lead": false,
            "job_type": {
              "id": "jt_02",
              "name": "Local move"
            },
            "notes": "Two flights of stairs",
            "number": 1138,
            "profit": 0.0,
            "quoted": 1450.0,
            "scheduled_end": "2026-10-16T11:00:00-05:00",
            "scheduled_start": "2026-10-16T09:00:00-05:00",
            "source": {
              "call_at": "2026-10-11T18:22:03Z",
              "call_id": "CAL8f2e1",
              "campaign": "Moving - Bloomington",
              "click_id": "Cj0KCQjw...",
              "filled_by": "call",
              "label": "Google Ads"
            },
            "status": {
              "id": "st_02",
              "name": "Scheduled",
              "since": "2026-10-12T15:04:10Z",
              "slug": "scheduled"
            },
            "technicians": [
              {
                "id": "wrk_3b90",
                "name": "Dana R."
              }
            ]
          }
        ],
        "next": "eyJhZnRlciI6IjIwMjYtMTAtMTFUMTg6MjU6NDBaIn0"
      },
      "exception": "",
      "group": "Jobs",
      "id": "list_jobs",
      "mcp": true,
      "mcp_why": "",
      "method": "GET",
      "need": "read",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "cmp_14f5",
          "in": "query",
          "kind": "id",
          "name": "company_id",
          "required": false,
          "says": "One company. Default: every company the credential reaches."
        },
        {
          "choices": [],
          "example": "scheduled",
          "in": "query",
          "kind": "text",
          "name": "status",
          "required": false,
          "says": "A status name or slug from Who am I."
        },
        {
          "choices": [],
          "example": "wrk_3b90",
          "in": "query",
          "kind": "id",
          "name": "worker_id",
          "required": false,
          "says": "Jobs this technician or subcontractor is on."
        },
        {
          "choices": [],
          "example": "2026-10-01",
          "in": "query",
          "kind": "date",
          "name": "from",
          "required": false,
          "says": "Created on or after this day, in the company's time zone."
        },
        {
          "choices": [],
          "example": "2026-10-31",
          "in": "query",
          "kind": "date",
          "name": "to",
          "required": false,
          "says": "Created on or before this day."
        },
        {
          "choices": [
            "include",
            "only",
            "exclude"
          ],
          "example": "include",
          "in": "query",
          "kind": "text",
          "name": "leads",
          "required": false,
          "says": "include (default), only, or exclude."
        },
        {
          "choices": [],
          "example": "50",
          "in": "query",
          "kind": "number",
          "name": "limit",
          "required": false,
          "says": "1 to 200. Default 50."
        },
        {
          "choices": [],
          "example": "eyJhZnRlciI6...",
          "in": "query",
          "kind": "text",
          "name": "cursor",
          "required": false,
          "says": "From the previous answer's next."
        }
      ],
      "path": "/api/v1/jobs",
      "returns": "items, one job each in the Get a job shape; next, a cursor for the following page or null; count, how many match in all. Trashed jobs are never listed.",
      "says": "Jobs and leads in the companies this credential reaches, newest first, with status, technician, schedule, money and the ad click each came from.",
      "source": "read side: view_job_row",
      "title": "List jobs"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": true,
        "title": "Get a job"
      },
      "area": "jobs",
      "command": "",
      "cost": "read",
      "example": {
        "address": {
          "city": "Bloomington",
          "line1": "2590 Main St",
          "line2": "",
          "one_line": "2590 Main St, Bloomington, IL 61701",
          "postal_code": "61701",
          "state": "IL"
        },
        "canceled_at": null,
        "client": {
          "id": "cli_77a0",
          "name": "R. Azoulay",
          "phone": "+12175550142",
          "second_phone": ""
        },
        "closed_at": null,
        "collected": 0.0,
        "company": {
          "id": "cmp_14f5",
          "name": "Sunrise Moving"
        },
        "created_at": "2026-10-11T18:25:40Z",
        "currency": "USD",
        "id": "job_1138",
        "is_lead": false,
        "job_type": {
          "id": "jt_02",
          "name": "Local move"
        },
        "notes": "Two flights of stairs",
        "number": 1138,
        "profit": 0.0,
        "quoted": 1450.0,
        "scheduled_end": "2026-10-16T11:00:00-05:00",
        "scheduled_start": "2026-10-16T09:00:00-05:00",
        "source": {
          "call_at": "2026-10-11T18:22:03Z",
          "call_id": "CAL8f2e1",
          "campaign": "Moving - Bloomington",
          "click_id": "Cj0KCQjw...",
          "filled_by": "call",
          "label": "Google Ads"
        },
        "status": {
          "id": "st_02",
          "name": "Scheduled",
          "since": "2026-10-12T15:04:10Z",
          "slug": "scheduled"
        },
        "technicians": [
          {
            "id": "wrk_3b90",
            "name": "Dana R."
          }
        ]
      },
      "exception": "",
      "group": "Jobs",
      "id": "get_job",
      "mcp": true,
      "mcp_why": "",
      "method": "GET",
      "need": "read",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "job_1138",
          "in": "path",
          "kind": "id",
          "name": "job_id",
          "required": true,
          "says": "The job id, or its number."
        }
      ],
      "path": "/api/v1/jobs/{job_id}",
      "returns": "The job. Money is the price quoted, what was collected, and the profit after the subcontractor, each as a number in the company's currency. source is how the job was attributed: filled_by says whether the click came off a tracked call or the job's own record.",
      "says": "One job or lead: who, where, when, which status, which technicians, the money on it and the ad click it came from.",
      "source": "read side: view_job_row",
      "title": "Get a job"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": true,
        "title": "A job's history"
      },
      "area": "jobs",
      "command": "",
      "cost": "read",
      "example": {
        "items": [
          {
            "at": "2026-10-11T18:25:40Z",
            "by": "Ariel T.",
            "event": "job.created",
            "facts": {
              "client_name": "R. Azoulay",
              "intent": "create"
            },
            "position": 88120
          },
          {
            "at": "2026-10-11T18:26:02Z",
            "by": "Ariel T.",
            "event": "job.assigned",
            "facts": {
              "worker_id": "wrk_3b90"
            },
            "position": 88131
          },
          {
            "at": "2026-10-12T15:04:10Z",
            "by": "Claude, for Ariel T.",
            "event": "job.note_added",
            "facts": {
              "body": "Gate code 4410"
            },
            "position": 88402
          }
        ]
      },
      "exception": "",
      "group": "Jobs",
      "id": "get_job_history",
      "mcp": true,
      "mcp_why": "",
      "method": "GET",
      "need": "read",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "job_1138",
          "in": "path",
          "kind": "id",
          "name": "job_id",
          "required": true,
          "says": "The job id, or its number."
        }
      ],
      "path": "/api/v1/jobs/{job_id}/history",
      "returns": "items, oldest first: position (the event position, usable as after= on a read), event, at, by (a person's name, or \"Claude, for Ariel T.\" when an AI app acted), and the event's own facts.",
      "says": "Everything that happened to a job, in order, with who did it: created, moved, assigned, noted, paid. The same log the job page shows.",
      "source": "events.event, stream job:<id>",
      "title": "A job's history"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": false,
        "title": "Create a job"
      },
      "area": "jobs",
      "command": "create-job",
      "cost": "write",
      "example": {
        "accepted": true,
        "job_id": "job_1203",
        "number": 1203,
        "position": 88411,
        "status": {
          "id": "st_02",
          "name": "Scheduled",
          "slug": "scheduled"
        }
      },
      "exception": "",
      "group": "Job commands",
      "id": "create_job",
      "mcp": true,
      "mcp_why": "",
      "method": "POST",
      "need": "do",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "cmp_14f5",
          "in": "body",
          "kind": "id",
          "name": "company_id",
          "required": true,
          "says": "From Who am I."
        },
        {
          "choices": [],
          "example": "R. Azoulay",
          "in": "body",
          "kind": "text",
          "name": "client_name",
          "required": true,
          "says": "The customer, as they gave it."
        },
        {
          "choices": [],
          "example": "+12175550142",
          "in": "body",
          "kind": "text",
          "name": "client_phone",
          "required": true,
          "says": "Any format; stored as E.164."
        },
        {
          "choices": [],
          "example": "{\"line1\": \"2590 Main St\", \"city\": \"Bloomington\", \"state\": \"IL\", \"postal_code\": \"61701\"}",
          "in": "body",
          "kind": "object",
          "name": "address",
          "required": true,
          "says": "line1, city, state and postal_code. line2 is optional."
        },
        {
          "choices": [],
          "example": "jt_02",
          "in": "body",
          "kind": "id",
          "name": "job_type_id",
          "required": false,
          "says": "From Who am I."
        },
        {
          "choices": [],
          "example": "2026-10-16T09:00:00-05:00",
          "in": "body",
          "kind": "datetime",
          "name": "scheduled_start",
          "required": false,
          "says": "ISO 8601, with scheduled_end."
        },
        {
          "choices": [],
          "example": "2026-10-16T11:00:00-05:00",
          "in": "body",
          "kind": "datetime",
          "name": "scheduled_end",
          "required": false,
          "says": "ISO 8601. The end of the window."
        },
        {
          "choices": [],
          "example": "1450",
          "in": "body",
          "kind": "money",
          "name": "quoted_price",
          "required": false,
          "says": "The price quoted, a number."
        },
        {
          "choices": [],
          "example": "wrk_3b90",
          "in": "body",
          "kind": "id",
          "name": "worker_id",
          "required": false,
          "says": "Assigns the technician now."
        },
        {
          "choices": [],
          "example": "Two flights of stairs",
          "in": "body",
          "kind": "text",
          "name": "notes",
          "required": false,
          "says": "Anything the crew should know."
        },
        {
          "choices": [
            "create",
            "draft",
            "dispatch"
          ],
          "example": "create",
          "in": "body",
          "kind": "text",
          "name": "intent",
          "required": false,
          "says": "create (default) opens a job; draft opens a lead; dispatch opens it and sends the ticket to the technician."
        }
      ],
      "path": "/api/v1/commands/create-job",
      "returns": "accepted, job_id, number, and position: the event position, usable as after= on the next read so you see your own write. A missing fact the app would hold on Draft is refused here with 422 naming the field.",
      "says": "Open a job or a lead in one company: the customer, the address, and optionally the job type, the time window and the technician.",
      "source": "",
      "title": "Create a job"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": false,
        "title": "Change a job"
      },
      "area": "jobs",
      "command": "update-job",
      "cost": "write",
      "example": {
        "accepted": true,
        "changed": [
          "scheduled_start",
          "scheduled_end",
          "quoted_price"
        ],
        "job_id": "job_1203",
        "number": 1203,
        "position": 88411
      },
      "exception": "",
      "group": "Job commands",
      "id": "update_job",
      "mcp": true,
      "mcp_why": "",
      "method": "POST",
      "need": "do",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "job_1203",
          "in": "body",
          "kind": "id",
          "name": "job_id",
          "required": true,
          "says": "The job id, or its number."
        },
        {
          "choices": [],
          "example": "",
          "in": "body",
          "kind": "text",
          "name": "client_name",
          "required": false,
          "says": "The customer."
        },
        {
          "choices": [],
          "example": "",
          "in": "body",
          "kind": "text",
          "name": "client_phone",
          "required": false,
          "says": "Any format; stored as E.164."
        },
        {
          "choices": [],
          "example": "",
          "in": "body",
          "kind": "text",
          "name": "client_secondary_phone",
          "required": false,
          "says": "A second phone, or empty to clear it."
        },
        {
          "choices": [],
          "example": "",
          "in": "body",
          "kind": "object",
          "name": "address",
          "required": false,
          "says": "All four parts, as for Create a job."
        },
        {
          "choices": [],
          "example": "",
          "in": "body",
          "kind": "id",
          "name": "job_type_id",
          "required": false,
          "says": "From Who am I."
        },
        {
          "choices": [],
          "example": "2026-10-16T14:00:00-05:00",
          "in": "body",
          "kind": "datetime",
          "name": "scheduled_start",
          "required": false,
          "says": "ISO 8601. Send null to clear the window."
        },
        {
          "choices": [],
          "example": "2026-10-16T17:00:00-05:00",
          "in": "body",
          "kind": "datetime",
          "name": "scheduled_end",
          "required": false,
          "says": "The end of the window."
        },
        {
          "choices": [],
          "example": "1600",
          "in": "body",
          "kind": "money",
          "name": "quoted_price",
          "required": false,
          "says": "The new price, a number."
        },
        {
          "choices": [],
          "example": "",
          "in": "body",
          "kind": "text",
          "name": "notes",
          "required": false,
          "says": "Replaces the notes."
        }
      ],
      "path": "/api/v1/commands/update-job",
      "returns": "accepted, job_id, number, position, and changed: the fields that moved.",
      "says": "Change a job's facts: the customer, the address, the schedule, the price, the job type or the notes. Only the fields you send change.",
      "source": "",
      "title": "Change a job"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": false,
        "title": "Move a job's status"
      },
      "area": "jobs",
      "command": "change-job-status",
      "cost": "write",
      "example": {
        "accepted": true,
        "job_id": "job_1203",
        "number": 1203,
        "position": 88411,
        "status": {
          "id": "st_03",
          "name": "Dispatched",
          "slug": "dispatched"
        }
      },
      "exception": "",
      "group": "Job commands",
      "id": "change_job_status",
      "mcp": true,
      "mcp_why": "",
      "method": "POST",
      "need": "do",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "job_1203",
          "in": "body",
          "kind": "id",
          "name": "job_id",
          "required": true,
          "says": "The job id, or its number."
        },
        {
          "choices": [],
          "example": "Dispatched",
          "in": "body",
          "kind": "text",
          "name": "status",
          "required": true,
          "says": "A status name or slug from Who am I. status_id works too."
        },
        {
          "choices": [],
          "example": "Customer found another mover",
          "in": "body",
          "kind": "text",
          "name": "reason",
          "required": false,
          "says": "Read on a move into Canceled: why."
        }
      ],
      "path": "/api/v1/commands/change-job-status",
      "returns": "accepted, job_id, number, position, and status: where the job now stands.",
      "says": "Move a job to another status by its name, slug or id. The move lands in the job's history with who asked for it.",
      "source": "",
      "title": "Move a job's status"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": false,
        "title": "Assign a technician"
      },
      "area": "jobs",
      "command": "assign-worker",
      "cost": "write",
      "example": {
        "accepted": true,
        "automation": "Ticket sent to Omar S.",
        "job_id": "job_1203",
        "number": 1203,
        "position": 88411,
        "technicians": [
          {
            "id": "wrk_7a12",
            "name": "Omar S."
          }
        ]
      },
      "exception": "",
      "group": "Job commands",
      "id": "assign_worker",
      "mcp": true,
      "mcp_why": "",
      "method": "POST",
      "need": "do",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "job_1203",
          "in": "body",
          "kind": "id",
          "name": "job_id",
          "required": true,
          "says": "The job id, or its number."
        },
        {
          "choices": [],
          "example": "wrk_7a12",
          "in": "body",
          "kind": "id",
          "name": "worker_id",
          "required": true,
          "says": "From Who am I."
        }
      ],
      "path": "/api/v1/commands/assign-worker",
      "returns": "accepted, job_id, number, position, technicians now on the job, and automation: one sentence on what the automation did, or empty.",
      "says": "Put a technician or subcontractor on a job. If Job follow-up is on, the automation acts on it at once, as it would from the app.",
      "source": "",
      "title": "Assign a technician"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": false,
        "title": "Unassign a technician"
      },
      "area": "jobs",
      "command": "unassign-worker",
      "cost": "write",
      "example": {
        "accepted": true,
        "job_id": "job_1203",
        "number": 1203,
        "position": 88411,
        "technicians": []
      },
      "exception": "",
      "group": "Job commands",
      "id": "unassign_worker",
      "mcp": true,
      "mcp_why": "",
      "method": "POST",
      "need": "do",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "job_1203",
          "in": "body",
          "kind": "id",
          "name": "job_id",
          "required": true,
          "says": "The job id, or its number."
        },
        {
          "choices": [],
          "example": "wrk_7a12",
          "in": "body",
          "kind": "id",
          "name": "worker_id",
          "required": true,
          "says": "From Who am I."
        }
      ],
      "path": "/api/v1/commands/unassign-worker",
      "returns": "accepted, job_id, number, position, and technicians still on the job.",
      "says": "Take a technician or subcontractor off a job. The ticket's cancellation reaches them the way it does from the app.",
      "source": "",
      "title": "Unassign a technician"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": false,
        "title": "Add a note"
      },
      "area": "jobs",
      "command": "add-job-note",
      "cost": "write",
      "example": {
        "accepted": true,
        "job_id": "job_1203",
        "note_id": "note_5c21",
        "number": 1203,
        "position": 88411
      },
      "exception": "",
      "group": "Job commands",
      "id": "add_job_note",
      "mcp": true,
      "mcp_why": "",
      "method": "POST",
      "need": "do",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "job_1203",
          "in": "body",
          "kind": "id",
          "name": "job_id",
          "required": true,
          "says": "The job id, or its number."
        },
        {
          "choices": [],
          "example": "Gate code 4410",
          "in": "body",
          "kind": "text",
          "name": "body",
          "required": true,
          "says": "The note, as written."
        }
      ],
      "path": "/api/v1/commands/add-job-note",
      "returns": "accepted, job_id, number, note_id and position.",
      "says": "Add a note to a job. It appends to the job's log and never overwrites.",
      "source": "",
      "title": "Add a note"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": true,
        "title": "List tables"
      },
      "area": "raw",
      "command": "",
      "cost": "read",
      "example": {
        "schemas": [
          {
            "available": true,
            "key": "google_ads",
            "label": "Google Ads",
            "tables": [
              {
                "columns": [
                  {
                    "key": "date",
                    "kind": "date",
                    "label": "Date"
                  },
                  {
                    "key": "campaign_name",
                    "kind": "text",
                    "label": "Campaign"
                  },
                  {
                    "key": "cost",
                    "kind": "money",
                    "label": "Spend"
                  },
                  {
                    "key": "clicks",
                    "kind": "number",
                    "label": "Clicks"
                  }
                ],
                "date_column": "date",
                "grain": "One row per campaign per day.",
                "key": "google_ads.spend_by_day",
                "name": "spend_by_day"
              }
            ]
          },
          {
            "available": true,
            "key": "callrail",
            "label": "CallRail",
            "tables": [
              {
                "columns": [
                  {
                    "key": "started_at",
                    "kind": "datetime",
                    "label": "Started"
                  },
                  {
                    "key": "customer_phone",
                    "kind": "text",
                    "label": "Caller"
                  },
                  {
                    "key": "has_identifier",
                    "kind": "boolean",
                    "label": "Click id"
                  }
                ],
                "date_column": "started_at",
                "grain": "One row per tracked call.",
                "key": "callrail.calls",
                "name": "calls"
              }
            ]
          }
        ]
      },
      "exception": "",
      "group": "Raw data",
      "id": "list_tables",
      "mcp": true,
      "mcp_why": "",
      "method": "GET",
      "need": "read",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "cmp_14f5",
          "in": "query",
          "kind": "id",
          "name": "company_id",
          "required": false,
          "says": "Default: the first company the credential reaches."
        }
      ],
      "path": "/api/v1/data/tables",
      "returns": "schemas, one per provider (google_ads, meta_ads, microsoft_ads, callrail, headhoncho), each with its tables: key, name, grain, caveat, columns (key, label, kind), the date column if any, and available.",
      "says": "Every raw table, by the provider that produced it, with its columns, its grain and whether this company has it connected.",
      "source": "api.views.tables.SCHEMAS",
      "title": "List tables"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": true,
        "title": "Read a table"
      },
      "area": "raw",
      "command": "",
      "cost": "read",
      "example": {
        "limit": 200,
        "offset": 0,
        "rows": [
          {
            "campaign_name": "Moving - Bloomington",
            "clicks": 41,
            "cost": 212.4,
            "currency": "USD",
            "date": "2026-10-13",
            "impressions": 1904
          },
          {
            "campaign_name": "Moving - Bloomington",
            "clicks": 37,
            "cost": 198.05,
            "currency": "USD",
            "date": "2026-10-12",
            "impressions": 1771
          }
        ],
        "table": {
          "caveat": "Spend is what Google reported for the day; it can move for 48 hours.",
          "grain": "One row per campaign per day.",
          "key": "google_ads.spend_by_day"
        },
        "total": 28
      },
      "exception": "",
      "group": "Raw data",
      "id": "read_table",
      "mcp": true,
      "mcp_why": "",
      "method": "GET",
      "need": "read",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "google_ads.spend_by_day",
          "in": "path",
          "kind": "text",
          "name": "key",
          "required": true,
          "says": "A table key from List tables."
        },
        {
          "choices": [],
          "example": "cmp_14f5",
          "in": "query",
          "kind": "id",
          "name": "company_id",
          "required": false,
          "says": "Default: the first company the credential reaches."
        },
        {
          "choices": [],
          "example": "2026-10-01",
          "in": "query",
          "kind": "date",
          "name": "from",
          "required": false,
          "says": "For tables with a date column. Default: this month."
        },
        {
          "choices": [],
          "example": "2026-10-14",
          "in": "query",
          "kind": "date",
          "name": "to",
          "required": false,
          "says": "The last day, inclusive."
        },
        {
          "choices": [],
          "example": "date",
          "in": "query",
          "kind": "text",
          "name": "order",
          "required": false,
          "says": "A column key. Default: the table's own order."
        },
        {
          "choices": [
            "asc",
            "desc"
          ],
          "example": "desc",
          "in": "query",
          "kind": "text",
          "name": "direction",
          "required": false,
          "says": "asc or desc."
        },
        {
          "choices": [],
          "example": "200",
          "in": "query",
          "kind": "number",
          "name": "limit",
          "required": false,
          "says": "1 to 1000. Default 200."
        },
        {
          "choices": [],
          "example": "0",
          "in": "query",
          "kind": "number",
          "name": "offset",
          "required": false,
          "says": "Rows to skip. Default 0."
        }
      ],
      "path": "/api/v1/data/tables/{key}",
      "returns": "table (its definition), rows, total, limit, offset, and the caveat the screen prints beside this table, because a row that leaves without its sentence will be misread.",
      "says": "Rows of one raw table for one company and a date range, exactly as the provider reported them. No joins, no derived figures.",
      "source": "api.views.tables.TABLES",
      "title": "Read a table"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": true,
        "title": "Download a table"
      },
      "area": "raw",
      "command": "",
      "cost": "export",
      "example": {
        "download": {
          "expires_in": 300,
          "filename": "callrail-calls-20261014-0915.csv",
          "url": "https://api.headhoncho.app/api/v1/data/tables/callrail.calls/export?company_id=cmp_14f5&from=2026-10-01&to=2026-10-14&once=k7Qm..."
        }
      },
      "exception": "",
      "group": "Raw data",
      "id": "export_table",
      "mcp": true,
      "mcp_why": "",
      "method": "GET",
      "need": "read",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "callrail.calls",
          "in": "path",
          "kind": "text",
          "name": "key",
          "required": true,
          "says": "A table key from List tables."
        },
        {
          "choices": [],
          "example": "cmp_14f5",
          "in": "query",
          "kind": "id",
          "name": "company_id",
          "required": false,
          "says": "Default: the first company the credential reaches."
        },
        {
          "choices": [],
          "example": "2026-10-01",
          "in": "query",
          "kind": "date",
          "name": "from",
          "required": false,
          "says": "Default: this month."
        },
        {
          "choices": [],
          "example": "2026-10-14",
          "in": "query",
          "kind": "date",
          "name": "to",
          "required": false,
          "says": "The last day, inclusive."
        }
      ],
      "path": "/api/v1/data/tables/{key}/export",
      "returns": "Over the API: the CSV itself (text/csv). Over MCP: a download link that works once for five minutes, so the file never passes through the AI.",
      "says": "One raw table as a CSV file for a company and a date range, up to 50,000 rows, with its caveat in the last lines.",
      "source": "api.views.tables.TABLES",
      "title": "Download a table"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": true,
        "title": "A job's conversion value"
      },
      "area": "conv",
      "command": "",
      "cost": "read",
      "example": {
        "click": {
          "call_at": "2026-10-11T18:22:03Z",
          "call_id": "CAL8f2e1",
          "id": "Cj0KCQjw...",
          "match": "matched",
          "network": "google_ads"
        },
        "conversions": [
          {
            "at": "2026-10-12T06:00:00Z",
            "based_on": "the quoted price",
            "goal": "Booked job",
            "stage": "booked",
            "state": "accepted",
            "state_label": "Accepted by Google",
            "value": 1450.0
          },
          {
            "at": null,
            "based_on": "profit after the subcontractor",
            "goal": "Paid job",
            "stage": "paid",
            "state": "waiting",
            "state_label": "Waiting for the first payment",
            "value": 610.0
          }
        ],
        "currency": "USD",
        "job": {
          "id": "job_1138",
          "number": 1138
        },
        "total_sent": 1450.0,
        "value_rules": {
          "booked_job": "quoted",
          "paid_job": "net_profit",
          "paid_when": "first_payment"
        }
      },
      "exception": "",
      "group": "Click IDs and conversions",
      "id": "get_job_conversion",
      "mcp": true,
      "mcp_why": "",
      "method": "GET",
      "need": "read",
      "notes": [
        [
          "No click, no conversion",
          "A job that did not come from an ad click answers click: null and a why, for example \"The call carried no click id\" or \"Entered by hand\". Nothing is sent for it, and conversions is empty."
        ]
      ],
      "params": [
        {
          "choices": [],
          "example": "job_1138",
          "in": "path",
          "kind": "id",
          "name": "job_id",
          "required": true,
          "says": "The job id, or its number."
        }
      ],
      "path": "/api/v1/jobs/{job_id}/conversion",
      "returns": "click: the network, the click id, the tracked call and its time, or null with why when the job came from no click. value_rules: how this company values a booked and a paid job. conversions: one per stage sent, with value, based_on, state (waiting, sent, accepted, refused, too_old, no_credits, taken_back) and at. total_sent is the sum Google accepted.",
      "says": "What this job told the ad platform it was worth: the click it came from, each conversion sent, its value and its basis, and what Google did with it.",
      "source": "read side: view_conversion_row, view_conversion_send, view_conversion_setting",
      "title": "A job's conversion value"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": true,
        "title": "Click ID report"
      },
      "area": "conv",
      "command": "",
      "cost": "read",
      "example": {
        "calls": 118,
        "calls_with_click_id": 69,
        "calls_without_click_id": 49,
        "from": "2026-10-01",
        "rows": [
          {
            "callrail_call_id": "CAL8f2e1",
            "callrail_call_start": "2026-10-11 13:22",
            "campaign_id": "2184930112",
            "close_date": "2026-10-16 11:40",
            "company_total": 610.0,
            "customer_phone": "+12175550142",
            "gclid": "Cj0KCQjw...",
            "job_source": "Google Ads",
            "lead_source": "call",
            "paid_date": "2026-10-16",
            "start_date": "2026-10-11 13:25",
            "status": "Closed",
            "ticket": 1138,
            "time_zone": "America/Chicago",
            "total": 1450.0
          }
        ],
        "to": "2026-10-31",
        "totals": {
          "closed": 41,
          "company_total": 28410.5,
          "jobs": 64,
          "total": 71240.0,
          "with_call": 52,
          "with_click_id": 37
        }
      },
      "exception": "Owner, 2026-10-14: the one report in the starter, because the file is handed to Google by the customer and must exist outside the app.",
      "group": "Click IDs and conversions",
      "id": "get_click_id_report",
      "mcp": true,
      "mcp_why": "",
      "method": "GET",
      "need": "read",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "2026-10-01",
          "in": "query",
          "kind": "date",
          "name": "from",
          "required": true,
          "says": "The first day. A job is in the range when it opened or closed in it."
        },
        {
          "choices": [],
          "example": "2026-10-31",
          "in": "query",
          "kind": "date",
          "name": "to",
          "required": true,
          "says": "The last day, inclusive."
        },
        {
          "choices": [],
          "example": "cmp_14f5",
          "in": "query",
          "kind": "id",
          "name": "company_id",
          "required": false,
          "says": "Default: every company the credential reaches."
        }
      ],
      "path": "/api/v1/reports/google-click-ids",
      "returns": "rows in the agency's fifteen columns, and totals: jobs, closed, with_click_id, with_call, total, company_total; plus calls, calls_with_click_id and calls_without_click_id for the range, because a call with no click id can never be in any file, whatever anyone does downstream.",
      "says": "The Google Click ID report: one row per job that opened or closed in the range, with its click id, its tracked call, its total and its profit.",
      "source": "api.views.reports.google_click_ids",
      "title": "Click ID report"
    },
    {
      "annotations": {
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false,
        "readOnlyHint": true,
        "title": "Download the Click ID file"
      },
      "area": "conv",
      "command": "",
      "cost": "export",
      "example": {
        "download": {
          "expires_in": 300,
          "filename": "google-ads-conversions-20261014-0915.csv",
          "url": "https://api.headhoncho.app/api/v1/reports/google-click-ids/export?from=2026-10-01&to=2026-10-31&output=google_ads&company_id=cmp_14f5&once=p2Xw..."
        }
      },
      "exception": "Owner, 2026-10-14: same decision as the report.",
      "group": "Click IDs and conversions",
      "id": "export_click_id_report",
      "mcp": true,
      "mcp_why": "",
      "method": "GET",
      "need": "read",
      "notes": [],
      "params": [
        {
          "choices": [],
          "example": "2026-10-01",
          "in": "query",
          "kind": "date",
          "name": "from",
          "required": true,
          "says": "The first day."
        },
        {
          "choices": [],
          "example": "2026-10-31",
          "in": "query",
          "kind": "date",
          "name": "to",
          "required": true,
          "says": "The last day, inclusive."
        },
        {
          "choices": [],
          "example": "cmp_14f5",
          "in": "query",
          "kind": "id",
          "name": "company_id",
          "required": false,
          "says": "For google_ads, the one account the file is for. Default: the first company."
        },
        {
          "choices": [
            "csv",
            "google_ads"
          ],
          "example": "csv",
          "in": "query",
          "kind": "text",
          "name": "output",
          "required": false,
          "says": "csv (the agency's fifteen columns, default) or google_ads (the upload file, one company at a time)."
        }
      ],
      "path": "/api/v1/reports/google-click-ids/export",
      "returns": "Over the API: the file (text/csv). Over MCP: a download link that works once for five minutes.",
      "says": "The Google Click ID report as a file: the agency's sheet, or the Google Ads offline conversion upload file, ready to upload.",
      "source": "api.views.reports.google_click_ids, commands.handlers.marketing.write_conversion_file",
      "title": "Download the Click ID file"
    }
  ],
  "errors": {
    "expired_token": {
      "message": "That token has expired. Refresh it, or connect the app again.",
      "status": 401
    },
    "idempotency_mismatch": {
      "message": "This Idempotency-Key was used with a different body.",
      "status": 409
    },
    "invalid": {
      "message": "Some fields were refused. See fields.",
      "status": 422
    },
    "missing_idempotency_key": {
      "message": "Send an Idempotency-Key header on every command.",
      "status": 400
    },
    "missing_key": {
      "message": "Send your key as Authorization: Bearer hh_live_...",
      "status": 401
    },
    "no_access": {
      "message": "This credential does not reach that. Ask the person who made it for access.",
      "status": 403
    },
    "not_connected": {
      "message": "This company has not connected that provider yet.",
      "status": 409
    },
    "not_found": {
      "message": "No such record in the companies this credential reaches.",
      "status": 404
    },
    "out_of_scope": {
      "message": "No such company in the companies this credential reaches.",
      "status": 404
    },
    "rate_limited": {
      "message": "Too many calls this minute. Wait and try again.",
      "status": 429
    },
    "read_only": {
      "message": "This credential may read this area but not change it.",
      "status": 403
    },
    "unknown_command": {
      "message": "No such command. GET /api/v1/ lists every one.",
      "status": 404
    },
    "unknown_endpoint": {
      "message": "No such endpoint. GET /api/v1/ lists every one.",
      "status": 404
    },
    "unknown_key": {
      "message": "That key is not one we issued, or it was revoked.",
      "status": 401
    }
  },
  "groups": [
    "Account",
    "Jobs",
    "Job commands",
    "Raw data",
    "Click IDs and conversions"
  ],
  "rate_per_minute": {
    "export": 10,
    "read": 300,
    "write": 60
  },
  "version": "v1"
}
