{
  "openapi": "3.1.0",
  "info": {
    "title": "VeraGen API",
    "version": "1.0.0",
    "summary": "Run and read your VeraGen AI Hub account from your own code.",
    "description": "The VeraGen API lets a program act on one VeraGen account: list and run its workflows, follow its runs, read its projects and Data Management items, create pages, list, read, create and update its Thrive contacts, read its Skunk Works items and sprints, read its Tellagent automations and enroll contacts in them, and read its billed usage. Every request is authenticated with an API key that an owner or admin creates in the hub under Account → API keys.",
    "contact": {
      "name": "VeraGen support",
      "email": "hello@veragen.ai",
      "url": "https://veragen.ai"
    },
    "x-live": false,
    "x-rate-limits": {
      "default_tier": "team",
      "window_seconds": 60,
      "failed_auth_per_minute_per_ip": 20,
      "tiers": {
        "solo": {
          "requests_per_minute_per_key": 60,
          "run_starts_per_minute_per_account": 6,
          "concurrent_runs_per_account": 2
        },
        "team": {
          "requests_per_minute_per_key": 120,
          "run_starts_per_minute_per_account": 12,
          "concurrent_runs_per_account": 5
        },
        "business": {
          "requests_per_minute_per_key": 300,
          "run_starts_per_minute_per_account": 30,
          "concurrent_runs_per_account": 10
        },
        "enterprise": {
          "requests_per_minute_per_key": 1000,
          "run_starts_per_minute_per_account": null,
          "concurrent_runs_per_account": null
        }
      }
    },
    "x-max-body-bytes": 1048576
  },
  "servers": [
    {
      "url": "https://api.veragen.ai/v1"
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "tags": [
    {
      "name": "AI Workflows",
      "description": "AI workflows are the AI agents of Tellagent: a chain of blocks (read pages, ask a model, wait for a person, write into a project) that runs on a schedule, from chat, by hand or through the API. List and read them here, across every project of the account; start and follow them under Runs. They are built in the hub, in Tellagent → AI Workflows."
    },
    {
      "name": "Runs",
      "description": "Start an AI workflow and follow it. Runs are asynchronous: starting one answers at once with a queued run, and the run begins within about a minute. Read it back with Get a run until its status is final."
    },
    {
      "name": "Automations",
      "description": "Automations are the other half of Tellagent: a trigger (a contact is created, a deal changes stage, a date comes round, a schedule, a webhook arrives, a text or an email reply comes in, an event in a connected Shopify, WooCommerce, Stripe, Calendly or Typeform account, an event in the project's Mercato store where Mercato is switched on, or a person enrolls contacts by hand) followed by steps that run for one contact at a time (a schedule, or a store event that is not about a customer, runs once with no contact, and its steps that need one skip): send an email or a text, wait, branch, tag, update the contact, notify the team. With `automations:read` a key lists them, across every project, and reads each one's runs. With `automations:enroll` it enrolls Thrive contacts in a published automation, exactly as Enroll contacts does in the hub, and **enrolling sends real messages**: the steps run within about a minute and email the contact, and text them where texting is set up. The API never creates, edits, publishes or pauses an automation, and returns a summary of its steps, never their settings.\n\nNobody is messaged against their wishes. A contact who was erased from Thrive is not enrolled (`erased`). A contact who is on do-not-contact, has opted out of email or unsubscribed is enrolled, as in the hub, but every email step skips them and says why in the run; a contact on do-not-contact, one who replied STOP, or one who has not agreed to texts (when the project asks for consent, which it does unless its Settings say otherwise) gets no texts either. Other steps, such as adding a tag or notifying the team, still run. A run started through the API says so in Run history, with the key's name."
    },
    {
      "name": "Usage",
      "description": "What the account has been billed, by day and by feature, and its balance."
    },
    {
      "name": "Projects",
      "description": "The projects in the account. Data Management items, and the workflows that write into them, belong to a project."
    },
    {
      "name": "Contacts",
      "description": "The people in Thrive, the account's CRM: list, read, create and update them. A key reaches every contact in the account. Contacts created through the API are unassigned until someone in Thrive assigns them. The API never deletes a contact, never lifts a do-not-contact or an email opt-out, and never sends anything."
    },
    {
      "name": "Items",
      "description": "What is in a project's Data Management: pages, files, datasets, images and links. Read any item's details and a page's text, and create pages. Files can be listed and described but not uploaded or downloaded in v1."
    },
    {
      "name": "Skunk Works",
      "description": "Skunk Works is the hub's project management: each project's work items (stories, tasks, bugs and epics), its backlog and its sprints. With the `skunkworks:read` scope a key reads them in every project of the account that has opened Skunk Works: the projects and their key prefixes, the items with their status, sprint, epic, labels, assignee and dates, one item with its description, its linked Data Management documents and its whole history (comments included), and the sprints with their totals and, once finished, their review. An item is named by its key, such as `WR-12`; a key with a project's old prefix still finds the item. The API is read-only for Skunk Works: items are created and changed in the hub, and reading never opens Skunk Works in a project that has not used it."
    }
  ],
  "x-tagGroups": [
    {
      "name": "Tellagent",
      "tags": [
        "AI Workflows",
        "Runs",
        "Automations"
      ]
    },
    {
      "name": "Data Management",
      "tags": [
        "Projects",
        "Items"
      ]
    },
    {
      "name": "Thrive",
      "tags": [
        "Contacts"
      ]
    },
    {
      "name": "Skunk Works",
      "tags": [
        "Skunk Works"
      ]
    },
    {
      "name": "Account",
      "tags": [
        "Usage"
      ]
    }
  ],
  "x-scopes": {
    "workflows:read": "List and read workflows and their runs.",
    "workflows:run": "Start workflow runs. Runs spend the account's AI credit.",
    "automations:read": "List and read automations and the runs of each contact through them.",
    "automations:enroll": "Add contacts to a published automation. The automation then sends what its steps say: emails, and texts where texting is set up.",
    "content:read": "Read projects and Data Management items.",
    "content:write": "Create pages in Data Management.",
    "contacts:read": "Read Thrive contacts.",
    "contacts:write": "Create and update Thrive contacts.",
    "skunkworks:read": "Read the account's Skunk Works items, sprints and epics, in every project.",
    "usage:read": "Read the account's billed usage."
  },
  "x-error-codes": [
    {
      "status": 400,
      "type": "invalid_request",
      "code": "invalid_parameter",
      "description": "A query parameter has a value it cannot take. `param` names it."
    },
    {
      "status": 400,
      "type": "invalid_request",
      "code": "unknown_parameter",
      "description": "A query parameter this endpoint does not accept. Unknown parameters are refused, never ignored."
    },
    {
      "status": 400,
      "type": "invalid_request",
      "code": "invalid_id",
      "description": "An id or code in the path, or an id parameter, that is not in the form the endpoint takes: a UUID, unless the endpoint's parameters name another form."
    },
    {
      "status": 400,
      "type": "invalid_request",
      "code": "invalid_cursor",
      "description": "The `cursor` is not one this endpoint gave out."
    },
    {
      "status": 400,
      "type": "invalid_request",
      "code": "invalid_json",
      "description": "The body is not valid JSON."
    },
    {
      "status": 400,
      "type": "invalid_request",
      "code": "missing_idempotency_key",
      "description": "Starting a run, or enrolling contacts in an automation, needs an `Idempotency-Key` header."
    },
    {
      "status": 400,
      "type": "invalid_request",
      "code": "missing_field",
      "description": "A required body field is missing. `param` names it."
    },
    {
      "status": 400,
      "type": "invalid_request",
      "code": "invalid_idempotency_key",
      "description": "The `Idempotency-Key` is not 8 to 64 printable characters without spaces."
    },
    {
      "status": 401,
      "type": "authentication",
      "code": "invalid_api_key",
      "description": "No API key, a malformed one, or one that does not exist."
    },
    {
      "status": 401,
      "type": "authentication",
      "code": "api_key_revoked",
      "description": "The key was revoked in the hub."
    },
    {
      "status": 401,
      "type": "authentication",
      "code": "api_key_expired",
      "description": "The key passed the expiry date it was created with."
    },
    {
      "status": 402,
      "type": "insufficient_credit",
      "code": "account_out_of_credit",
      "description": "The account is out of AI credit, so no run was started. An owner, or an admin with billing access, adds credit in Account → Billing."
    },
    {
      "status": 402,
      "type": "insufficient_credit",
      "code": "key_spend_cap_reached",
      "description": "This key has used its monthly spend cap. An owner or admin can raise it in Account → API keys."
    },
    {
      "status": 403,
      "type": "permission",
      "code": "insufficient_scope",
      "description": "The key does not hold the scope this endpoint needs. The message names it."
    },
    {
      "status": 403,
      "type": "permission",
      "code": "account_suspended",
      "description": "The account is suspended. Contact VeraGen support."
    },
    {
      "status": 403,
      "type": "permission",
      "code": "account_closed",
      "description": "The account has been closed."
    },
    {
      "status": 403,
      "type": "permission",
      "code": "subscription_inactive",
      "description": "The account's plan has ended or lapsed, so it is read-only: reads still work, and anything that writes or starts a run is refused until an owner renews the plan in the hub."
    },
    {
      "status": 404,
      "type": "not_found",
      "code": "resource_not_found",
      "description": "Nothing with that id in this account. An id that belongs to another account gives exactly this answer."
    },
    {
      "status": 404,
      "type": "not_found",
      "code": "route_not_found",
      "description": "No endpoint at that path."
    },
    {
      "status": 404,
      "type": "not_found",
      "code": "api_not_available",
      "description": "The API is not open on this service yet. Every request gets this answer until it is."
    },
    {
      "status": 405,
      "type": "invalid_request",
      "code": "method_not_allowed",
      "description": "The path exists but not with that method. The `Allow` header lists the methods it takes."
    },
    {
      "status": 409,
      "type": "conflict",
      "code": "idempotency_in_progress",
      "description": "A request with this `Idempotency-Key` is still being handled. Retry with the same key in a few seconds."
    },
    {
      "status": 409,
      "type": "conflict",
      "code": "idempotency_outcome_unknown",
      "description": "The first request with this `Idempotency-Key` did not finish (it timed out or failed part way), so whether it started the run, enrolled the contacts or created the page is unknown. It is never run again: list the workflow's or the automation's runs, or the project's items, to see whether it did, and use a new key if it did not."
    },
    {
      "status": 409,
      "type": "conflict",
      "code": "automation_not_published",
      "description": "The automation is a draft or paused, so no one can be enrolled in it. Publish it (or resume it) in Tellagent first."
    },
    {
      "status": 409,
      "type": "conflict",
      "code": "contact_exists",
      "description": "Another contact in this account already has that email address. `resource_id` is its id: update it with PATCH instead."
    },
    {
      "status": 409,
      "type": "conflict",
      "code": "contact_do_not_contact",
      "description": "The address belongs to someone who was erased from Thrive at their request. They stay on do-not-contact: the API cannot add them again or change them."
    },
    {
      "status": 409,
      "type": "conflict",
      "code": "opt_out_locked",
      "description": "The contact has opted out of email. The API cannot opt them back in; a person can, in Thrive."
    },
    {
      "status": 413,
      "type": "invalid_request",
      "code": "body_too_large",
      "description": "The body is larger than 1 MB."
    },
    {
      "status": 413,
      "type": "invalid_request",
      "code": "page_too_large",
      "description": "The page would be larger than a page can be once converted to HTML. Split it into two pages."
    },
    {
      "status": 415,
      "type": "invalid_request",
      "code": "unsupported_media_type",
      "description": "A body that is not `application/json`."
    },
    {
      "status": 422,
      "type": "invalid_request",
      "code": "unknown_field",
      "description": "The body has a field this endpoint does not take. `param` names it. Unknown fields are refused, never ignored."
    },
    {
      "status": 422,
      "type": "invalid_request",
      "code": "invalid_body",
      "description": "The body is not a JSON object."
    },
    {
      "status": 422,
      "type": "invalid_request",
      "code": "invalid_field",
      "description": "A body field has a value it cannot take. `param` names it."
    },
    {
      "status": 422,
      "type": "invalid_request",
      "code": "workflow_empty",
      "description": "The workflow has no blocks, so there is nothing to run."
    },
    {
      "status": 422,
      "type": "invalid_request",
      "code": "idempotency_key_reused",
      "description": "This `Idempotency-Key` was already used for a different request. Use a new key for a new request."
    },
    {
      "status": 429,
      "type": "rate_limit",
      "code": "rate_limited",
      "description": "Too many requests for this key in the current minute. Wait for `Retry-After` seconds."
    },
    {
      "status": 429,
      "type": "rate_limit",
      "code": "too_many_failed_auth",
      "description": "Too many requests with a wrong key from one address in a minute."
    },
    {
      "status": 429,
      "type": "rate_limit",
      "code": "run_rate_limited",
      "description": "The account has started as many runs through the API in the last minute as its plan allows."
    },
    {
      "status": 429,
      "type": "rate_limit",
      "code": "concurrency_limited",
      "description": "The account already has as many API runs queued or running as its plan allows. Start another when one finishes."
    },
    {
      "status": 500,
      "type": "api_error",
      "code": "internal_error",
      "description": "Something went wrong on our side. Retry with backoff; quote the `request_id` to support."
    }
  ],
  "paths": {
    "/workflows": {
      "get": {
        "operationId": "listWorkflows",
        "tags": [
          "AI Workflows"
        ],
        "summary": "List workflows",
        "description": "Every workflow in the account, across all its projects, most recently updated first. Each one carries its most recent run, if it has been run.",
        "x-scope": "workflows:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only workflows with this status.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "active",
                "paused"
              ]
            }
          },
          {
            "name": "project_id",
            "in": "query",
            "required": false,
            "description": "Only the workflows in this project.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of workflows.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowList"
                },
                "example": {
                  "data": [
                    {
                      "id": "3f6c1d2e-8a4b-4f7e-9c21-5d0e7a1b2c3d",
                      "name": "Weekly competitor digest",
                      "description": "Reads five competitor blogs and writes a summary page.",
                      "status": "active",
                      "project_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                      "updated_at": "2026-10-02T15:04:05.123Z",
                      "last_run": {
                        "id": "c0ffee00-1234-4abc-9def-0123456789ab",
                        "status": "succeeded",
                        "at": "2026-10-03T06:00:00.000Z"
                      }
                    },
                    {
                      "id": "7b2e9f40-6c1d-4e8a-b3f5-2a9d0c7e1f64",
                      "name": "Lead enrichment",
                      "description": "",
                      "status": "draft",
                      "project_id": null,
                      "updated_at": "2026-09-30T09:12:44.000Z",
                      "last_run": null
                    }
                  ],
                  "has_more": true,
                  "next_cursor": "WyIyMDI2LTA5LTMwVDA5OjEyOjQ0LjAwMDAwMFoiLCI3YjJlOWY0MC02YzFkLTRlOGEtYjNmNS0yYTlkMGM3ZTFmNjQiXQ"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/workflows/{id}": {
      "get": {
        "operationId": "getWorkflow",
        "tags": [
          "AI Workflows"
        ],
        "summary": "Get a workflow",
        "description": "One workflow, with its most recent run.",
        "x-scope": "workflows:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkflowId"
          }
        ],
        "responses": {
          "200": {
            "description": "The workflow.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workflow"
                },
                "example": {
                  "id": "3f6c1d2e-8a4b-4f7e-9c21-5d0e7a1b2c3d",
                  "name": "Weekly competitor digest",
                  "description": "Reads five competitor blogs and writes a summary page.",
                  "status": "active",
                  "project_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                  "updated_at": "2026-10-02T15:04:05.123Z",
                  "last_run": {
                    "id": "c0ffee00-1234-4abc-9def-0123456789ab",
                    "status": "succeeded",
                    "at": "2026-10-03T06:00:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/workflows/{id}/runs": {
      "post": {
        "operationId": "startRun",
        "tags": [
          "Runs"
        ],
        "summary": "Start a run",
        "description": "Queues a run of the workflow and answers at once with `202` and the queued run. The run begins within about a minute and runs for up to two minutes; follow it with Get a run. It runs with everything in the account the key can reach, and its AI use is billed to the account like any other run, under Tellagent. A key with a monthly spend cap stops a run that reaches it.\n\n**`Idempotency-Key` is required.** Send a new unique value (a UUID is ideal) for each run you mean to start, and the same value again if you retry: a retry with the same key and the same request returns the first answer, with `Idempotent-Replay: true`, and never starts a second run. If the first request did not finish, the retry answers `409 idempotency_outcome_unknown` and nothing is run again; list the workflow's runs to see whether it started. Keys are remembered for 24 hours.\n\nRuns take no inputs in v1, so the body is `{}` or empty. A run that stops at a Human checkpoint waits for someone to approve it in the hub.",
        "x-scope": "workflows:run",
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkflowId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "description": "Empty, or `{}`.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StartRunRequest"
              },
              "example": {}
            }
          }
        },
        "responses": {
          "202": {
            "description": "The run is queued.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Idempotent-Replay": {
                "$ref": "#/components/headers/Idempotent-Replay"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RunStarted"
                },
                "example": {
                  "id": "4b7e1c2d-3f5a-4b6c-8d7e-9f0a1b2c3d4e",
                  "workflow_id": "3f6c1d2e-8a4b-4f7e-9c21-5d0e7a1b2c3d",
                  "status": "queued",
                  "trigger": "api",
                  "created_at": "2026-10-03T14:20:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "missing_idempotency_key",
                    "message": "Send an Idempotency-Key header (8 to 64 characters) with every run you start, and the same one again if you retry it.",
                    "param": "Idempotency-Key",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Not enough credit to start a run.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "insufficient_credit",
                    "code": "account_out_of_credit",
                    "message": "This account is out of AI credit. An owner, or an admin with billing access, can add credit in Account → Billing.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the workflows:run scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No workflow with that id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The Idempotency-Key is busy, or its first request did not finish.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "conflict",
                    "code": "idempotency_outcome_unknown",
                    "message": "The first request with this Idempotency-Key did not finish, so whether it started a run is unknown. It will not be run again. List the workflow's runs to see whether it did.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "422": {
            "description": "The body, the workflow or the Idempotency-Key cannot be used.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "idempotency_key_reused",
                    "message": "This Idempotency-Key was already used for a different request. Use a new key for a new request.",
                    "param": "Idempotency-Key",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests, or too many runs.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "rate_limit",
                    "code": "concurrency_limited",
                    "message": "This account already has 5 API runs queued or running. Start another when one finishes.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "get": {
        "operationId": "listRuns",
        "tags": [
          "Runs"
        ],
        "summary": "List a workflow's runs",
        "description": "The workflow's runs, newest first, however they were started: from the hub, on a schedule, by chat or through the API.",
        "x-scope": "workflows:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/WorkflowId"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of runs.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RunList"
                },
                "example": {
                  "data": [
                    {
                      "id": "4b7e1c2d-3f5a-4b6c-8d7e-9f0a1b2c3d4e",
                      "workflow_id": "3f6c1d2e-8a4b-4f7e-9c21-5d0e7a1b2c3d",
                      "status": "succeeded",
                      "trigger": "api",
                      "summary": "Completed 3 steps (3 ran).",
                      "created_at": "2026-10-03T14:20:00.000Z",
                      "started_at": "2026-10-03T14:20:00.000Z",
                      "finished_at": "2026-10-03T14:21:04.512Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/runs/{id}": {
      "get": {
        "operationId": "getRun",
        "tags": [
          "Runs"
        ],
        "summary": "Get a run",
        "description": "A run, its blocks and what it was billed. Poll it (every few seconds is plenty) until `status` is final: `succeeded`, `failed`, `partial` (stopped at a time, step or spend limit), `rejected` or `cancelled`. `awaiting_approval` means it stopped at a Human checkpoint and waits for someone in the hub.",
        "x-scope": "workflows:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/RunId"
          }
        ],
        "responses": {
          "200": {
            "description": "The run.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Run"
                },
                "example": {
                  "id": "4b7e1c2d-3f5a-4b6c-8d7e-9f0a1b2c3d4e",
                  "workflow_id": "3f6c1d2e-8a4b-4f7e-9c21-5d0e7a1b2c3d",
                  "status": "succeeded",
                  "trigger": "api",
                  "summary": "Completed 3 steps (3 ran).",
                  "created_at": "2026-10-03T14:20:00.000Z",
                  "started_at": "2026-10-03T14:20:00.000Z",
                  "finished_at": "2026-10-03T14:21:04.512Z",
                  "billed_micros": 41250,
                  "steps": [
                    {
                      "node_id": "n1",
                      "node_kind": "source.web",
                      "status": "succeeded",
                      "ms": 8120,
                      "error": null
                    },
                    {
                      "node_id": "n2",
                      "node_kind": "agent",
                      "status": "succeeded",
                      "ms": 15433,
                      "error": null
                    },
                    {
                      "node_id": "n3",
                      "node_kind": "output.project",
                      "status": "succeeded",
                      "ms": 210,
                      "error": null
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No run with that id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/automations": {
      "get": {
        "operationId": "listAutomations",
        "tags": [
          "Automations"
        ],
        "summary": "List automations",
        "description": "Every automation in the account, across all its projects, most recently changed first: drafts, published and paused ones. Each carries its trigger, a summary of its steps (how many, which kinds, what it sends) and how many contacts it has run for. The steps' settings (addresses, message text, webhook URLs) are never included.",
        "x-scope": "automations:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only automations with this status.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "published",
                "paused"
              ]
            }
          },
          {
            "name": "project_id",
            "in": "query",
            "required": false,
            "description": "Only the automations in this project.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of automations.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutomationList"
                },
                "example": {
                  "data": [
                    {
                      "id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
                      "project_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                      "name": "Speed to lead",
                      "status": "published",
                      "trigger": {
                        "type": "contact.created",
                        "label": "Contact created"
                      },
                      "published_version": 3,
                      "has_unpublished_changes": false,
                      "step_count": 5,
                      "step_types": [
                        "email.send",
                        "notify.team",
                        "wait",
                        "task.create",
                        "end"
                      ],
                      "sends": [
                        "email",
                        "team"
                      ],
                      "enrollments": {
                        "total": 214,
                        "active": 9,
                        "completed": 198
                      },
                      "last_run_at": "2026-10-07T16:42:10.000Z",
                      "created_at": "2026-09-02T10:00:00.000Z",
                      "updated_at": "2026-10-01T08:15:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "invalid_parameter",
                    "message": "status must be one of: draft, published, paused.",
                    "param": "status",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the automations:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No project with that id.",
                    "param": "project_id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/automations/{id}": {
      "get": {
        "operationId": "getAutomation",
        "tags": [
          "Automations"
        ],
        "summary": "Get an automation",
        "description": "One automation: its status, trigger, step summary and run counts.",
        "x-scope": "automations:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/AutomationId"
          }
        ],
        "responses": {
          "200": {
            "description": "The automation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Automation"
                },
                "example": {
                  "id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
                  "project_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                  "name": "Speed to lead",
                  "status": "published",
                  "trigger": {
                    "type": "contact.created",
                    "label": "Contact created"
                  },
                  "published_version": 3,
                  "has_unpublished_changes": false,
                  "step_count": 5,
                  "step_types": [
                    "email.send",
                    "notify.team",
                    "wait",
                    "task.create",
                    "end"
                  ],
                  "sends": [
                    "email",
                    "team"
                  ],
                  "enrollments": {
                    "total": 214,
                    "active": 9,
                    "completed": 198
                  },
                  "last_run_at": "2026-10-07T16:42:10.000Z",
                  "created_at": "2026-09-02T10:00:00.000Z",
                  "updated_at": "2026-10-01T08:15:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "invalid_id",
                    "message": "That is not a valid automation id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the automations:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No automation with that id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/automations/{id}/enrollments": {
      "post": {
        "operationId": "enrollContacts",
        "tags": [
          "Automations"
        ],
        "summary": "Enroll contacts",
        "description": "Adds Thrive contacts to a published automation, exactly as Enroll contacts does in the hub, and answers at once with who was enrolled and who was skipped, and why. Each enrolled contact gets a run; its steps begin within about a minute and **send real messages**: the emails the automation sends, and its texts where texting is set up. Follow them with List an automation's runs. Steps that use AI are billed to the account like any other AI use.\n\nA contact is skipped, not refused, when it is not in this account or is archived (`not_found`), was erased from Thrive at their request (`erased`), is already in this automation (`already_enrolled`), has been through it before and the automation runs once per contact (`enrolled_before`), or is already in as many automations at once as the project allows (`too_many_automations`). Contacts on do-not-contact or opted out are enrolled, and the steps that would message them are skipped (see the Automations section).\n\nThe automation must be published: a draft or a paused one answers `409 automation_not_published` and enrolls nobody. The trigger does not matter, and its Run only if filter is not applied: enrolling by hand is an instruction, as in the hub.\n\n**`Idempotency-Key` is required.** Send a new unique value for each request and the same value again if you retry: a retry with the same key and the same body returns the first answer, with `Idempotent-Replay: true`, and enrolls nobody twice. If the first request did not finish, the retry answers `409 idempotency_outcome_unknown` and nothing is done again; list the automation's runs to see whether it did. Keys are remembered for 24 hours. Run history shows each run as started through the API, with the key's name.",
        "x-scope": "automations:enroll",
        "parameters": [
          {
            "$ref": "#/components/parameters/AutomationId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyEnroll"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The contacts to enroll.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnrollRequest"
              },
              "example": {
                "contact_ids": [
                  "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                  "2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Who was enrolled, and who was skipped.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Idempotent-Replay": {
                "$ref": "#/components/headers/Idempotent-Replay"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnrollResult"
                },
                "example": {
                  "automation_id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
                  "enrolled": 1,
                  "runs": [
                    {
                      "id": "7b2c3d4e-5f60-4b7c-9d8e-0f1a2b3c4d5e",
                      "contact_id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"
                    }
                  ],
                  "skipped": [
                    {
                      "contact_id": "2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a",
                      "reason": "already_enrolled"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "missing_idempotency_key",
                    "message": "Send an Idempotency-Key header (8 to 64 characters) with every enrollment request, and the same one again if you retry it.",
                    "param": "Idempotency-Key",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the automations:enroll scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No automation with that id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The automation is not published, or the Idempotency-Key is busy or its first request did not finish.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "conflict",
                    "code": "automation_not_published",
                    "message": "This automation is a draft, so no one can be enrolled in it. Publish it in Tellagent first.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "422": {
            "description": "The body or the Idempotency-Key cannot be used.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "invalid_field",
                    "message": "contact_ids must be a list of 1 to 500 contact ids.",
                    "param": "contact_ids",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/automations/{id}/runs": {
      "get": {
        "operationId": "listAutomationRuns",
        "tags": [
          "Automations"
        ],
        "summary": "List an automation's runs",
        "description": "The automation's runs, one per contact each time they went through it, newest first, however they started: by its trigger, enrolled in the hub, or through the API. These are the rows Run history shows. Test runs from the builder are included, with `test: true`.",
        "x-scope": "automations:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/AutomationId"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only runs with this status.",
            "schema": {
              "type": "string",
              "enum": [
                "running",
                "waiting",
                "succeeded",
                "failed",
                "stopped",
                "skipped"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of runs.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutomationRunList"
                },
                "example": {
                  "data": [
                    {
                      "id": "7b2c3d4e-5f60-4b7c-9d8e-0f1a2b3c4d5e",
                      "automation_id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
                      "contact": {
                        "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                        "name": "Maya Lindqvist"
                      },
                      "status": "waiting",
                      "status_label": "Waiting until 2026-10-09 16:42 UTC",
                      "started_by": "Enrolled through the API by key “Website sign-ups” (vg_live_3fa9…)",
                      "test": false,
                      "version": 3,
                      "started_at": "2026-10-07T16:42:10.000Z",
                      "finished_at": null,
                      "next_at": "2026-10-09T16:42:10.000Z",
                      "error": null
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "invalid_parameter",
                    "message": "status must be one of: running, waiting, succeeded, failed, stopped, skipped.",
                    "param": "status",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the automations:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No automation with that id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/automation-runs/{id}": {
      "get": {
        "operationId": "getAutomationRun",
        "tags": [
          "Automations"
        ],
        "summary": "Get an automation run",
        "description": "One run of an automation, with each step it has run: its name, kind, status, how long it took and any error. A step that was skipped (an email to someone who unsubscribed, a text to someone who has not agreed to texts) is `skipped`. What a step sent (addresses, subject, text) is not included.",
        "x-scope": "automations:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/AutomationRunId"
          }
        ],
        "responses": {
          "200": {
            "description": "The run.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutomationRunDetail"
                },
                "example": {
                  "id": "7b2c3d4e-5f60-4b7c-9d8e-0f1a2b3c4d5e",
                  "automation_id": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
                  "contact": {
                    "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                    "name": "Maya Lindqvist"
                  },
                  "status": "waiting",
                  "status_label": "Waiting until 2026-10-09 16:42 UTC",
                  "started_by": "Enrolled through the API by key “Website sign-ups” (vg_live_3fa9…)",
                  "test": false,
                  "version": 3,
                  "started_at": "2026-10-07T16:42:10.000Z",
                  "finished_at": null,
                  "next_at": "2026-10-09T16:42:10.000Z",
                  "error": null,
                  "failed_step_id": null,
                  "steps": [
                    {
                      "step_id": "t",
                      "name": "Contact created",
                      "type": "trigger",
                      "status": "ok",
                      "started_at": "2026-10-07T16:42:10.000Z",
                      "ms": 0,
                      "error": null
                    },
                    {
                      "step_id": "s1",
                      "name": "Email: thanks for getting in touch",
                      "type": "email.send",
                      "status": "ok",
                      "started_at": "2026-10-07T16:42:31.000Z",
                      "ms": 412,
                      "error": null
                    },
                    {
                      "step_id": "s2",
                      "name": "Tell the owner",
                      "type": "notify.team",
                      "status": "ok",
                      "started_at": "2026-10-07T16:42:32.000Z",
                      "ms": 268,
                      "error": null
                    },
                    {
                      "step_id": "s3",
                      "name": "Wait 2 days",
                      "type": "wait",
                      "status": "waiting",
                      "started_at": "2026-10-07T16:42:32.000Z",
                      "ms": 0,
                      "error": null
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "invalid_id",
                    "message": "That is not a valid run id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the automations:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No run with that id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "tags": [
          "Usage"
        ],
        "summary": "Get usage",
        "description": "What the account was billed between two dates (UTC, both included), in total, per day and per feature, and its balance now. The default is this month so far. Amounts are what the account is charged, in micros (1,000,000 = US$1); the `_usd` strings are the same amounts rounded to the cent. Only days with usage are listed. At most 366 days per request.",
        "x-scope": "usage:read",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "First day, YYYY-MM-DD (UTC). Default: the first of this month.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Last day, YYYY-MM-DD (UTC), included. Default: today.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Usage for the window.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                },
                "example": {
                  "from": "2026-10-01",
                  "to": "2026-10-03",
                  "currency": "usd",
                  "total_micros": 2731500,
                  "total_usd": "2.73",
                  "balance_micros": 47268500,
                  "balance_usd": "47.27",
                  "by_day": [
                    {
                      "day": "2026-10-01",
                      "micros": 912000
                    },
                    {
                      "day": "2026-10-02",
                      "micros": 1384250
                    },
                    {
                      "day": "2026-10-03",
                      "micros": 435250
                    }
                  ],
                  "by_feature": [
                    {
                      "feature": "workflow",
                      "micros": 1953000
                    },
                    {
                      "feature": "studio_chat",
                      "micros": 640500
                    },
                    {
                      "feature": "compute",
                      "micros": 138000
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "invalid_parameter",
                    "message": "from must be a date written YYYY-MM-DD.",
                    "param": "from",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the usage:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No endpoint at that path.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "route_not_found",
                    "message": "There is no endpoint at GET /v1/usages.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects": {
      "get": {
        "operationId": "listProjects",
        "tags": [
          "Projects"
        ],
        "summary": "List projects",
        "description": "The account's projects, newest first. Archived projects are left out; read one by its id to see it.",
        "x-scope": "content:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of projects.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectList"
                },
                "example": {
                  "data": [
                    {
                      "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                      "name": "Market research",
                      "description": "Competitors, pricing and interviews.",
                      "archived": false,
                      "created_at": "2026-08-14T09:30:00.000Z",
                      "updated_at": "2026-10-02T15:04:05.123Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the content:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No endpoint at that path.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "route_not_found",
                    "message": "There is no endpoint at GET /v1/project.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}": {
      "get": {
        "operationId": "getProject",
        "tags": [
          "Projects"
        ],
        "summary": "Get a project",
        "description": "One project, archived or not.",
        "x-scope": "content:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectId"
          }
        ],
        "responses": {
          "200": {
            "description": "The project.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                },
                "example": {
                  "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                  "name": "Market research",
                  "description": "Competitors, pricing and interviews.",
                  "archived": false,
                  "created_at": "2026-08-14T09:30:00.000Z",
                  "updated_at": "2026-10-02T15:04:05.123Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the content:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No project with that id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}/items": {
      "get": {
        "operationId": "listItems",
        "tags": [
          "Items"
        ],
        "summary": "List a project's items",
        "description": "The items in a project's Data Management, newest first, in every folder unless `folder_id` is given. Deleted items are left out. A list item carries no body: read a page with Get an item.",
        "x-scope": "content:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectId"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Only items of this kind.",
            "schema": {
              "type": "string",
              "enum": [
                "page",
                "file",
                "dataset",
                "image",
                "link"
              ]
            }
          },
          {
            "name": "folder_id",
            "in": "query",
            "required": false,
            "description": "Only the items directly in this folder (not in its subfolders). Items carry their `folder_id`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Only items whose name contains this text, ignoring case (1 to 120 characters).",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 120
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of items.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ItemList"
                },
                "example": {
                  "data": [
                    {
                      "id": "5d4c3b2a-1f0e-4d9c-8b7a-6e5f4d3c2b1a",
                      "project_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                      "folder_id": null,
                      "name": "Competitor digest",
                      "kind": "page",
                      "ext": "html",
                      "size_bytes": 1834,
                      "status": "ready",
                      "grounding": true,
                      "created_at": "2026-10-03T06:01:12.000Z",
                      "updated_at": "2026-10-03T06:01:12.000Z"
                    },
                    {
                      "id": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
                      "project_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                      "folder_id": "8f7e6d5c-4b3a-4c2d-9e1f-0a9b8c7d6e5f",
                      "name": "Pricing survey.pdf",
                      "kind": "file",
                      "ext": "pdf",
                      "size_bytes": 482113,
                      "status": "ready",
                      "grounding": true,
                      "created_at": "2026-09-20T11:00:00.000Z",
                      "updated_at": "2026-09-20T11:02:40.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the content:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No folder with that id in this project.",
                    "param": "folder_id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "createPage",
        "tags": [
          "Items"
        ],
        "summary": "Create a page",
        "description": "Creates a page in the project's Data Management, at the top of the project or in `folder_id`, and answers `201` with it. Send the text as Markdown or HTML. It is cleaned exactly as a page saved in the hub is: scripts, event handlers, styles and anything else a page cannot hold are removed, so what you read back can differ from what you sent. Markdown is stored as HTML, so the page reads back with `format: \"html\"`. The page has a first version in its history, and is searched by Chat & Research and Odin like any other page once it has been indexed, which is billed as indexing any page is.\n\n`Idempotency-Key` is optional here and recommended: a retry with the same key and the same request returns the first answer, with `Idempotent-Replay: true`, and never makes a second page. Files cannot be uploaded in v1.",
        "x-scope": "content:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyOptional"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The page.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePageRequest"
              },
              "example": {
                "name": "Q3 board summary",
                "format": "markdown",
                "content": "# Q3 board summary\n\nRevenue grew **12%** on Q2.",
                "folder_id": null
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The page was created.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Idempotent-Replay": {
                "$ref": "#/components/headers/Idempotent-Replay"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Item"
                },
                "example": {
                  "id": "5d4c3b2a-1f0e-4d9c-8b7a-6e5f4d3c2b1a",
                  "project_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                  "folder_id": null,
                  "name": "Q3 board summary",
                  "kind": "page",
                  "ext": "html",
                  "size_bytes": 72,
                  "status": "ready",
                  "grounding": true,
                  "created_at": "2026-10-03T06:01:12.000Z",
                  "updated_at": "2026-10-03T06:01:12.000Z",
                  "body": {
                    "format": "html",
                    "content": "<h1>Q3 board summary</h1>\n<p>Revenue grew <strong>12%</strong> on Q2.</p>\n"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "missing_field",
                    "message": "content is required.",
                    "param": "content",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the content:write scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No project with that id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The Idempotency-Key is busy, or its first request did not finish.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "conflict",
                    "code": "idempotency_in_progress",
                    "message": "A request with this Idempotency-Key is still being handled. Retry in a few seconds.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "413": {
            "description": "The page is too large.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "page_too_large",
                    "message": "This page is too large to store (more than 900000 bytes once converted to HTML). Split it into two pages.",
                    "param": "content",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "422": {
            "description": "A body field cannot be used.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "unknown_field",
                    "message": "A page takes name, format, content and folder_id; \"title\" is not one of them.",
                    "param": "title",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/items/{id}": {
      "get": {
        "operationId": "getItem",
        "tags": [
          "Items"
        ],
        "summary": "Get an item",
        "description": "One item. For a page, `body` holds its text, as HTML (or Markdown, for a page that was stored as Markdown); images in a page are references (`<img data-vg-item=\"…\">`), not links. For a file, dataset, image or link, `body` is null in v1.",
        "x-scope": "content:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/ItemId"
          }
        ],
        "responses": {
          "200": {
            "description": "The item.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Item"
                },
                "example": {
                  "id": "5d4c3b2a-1f0e-4d9c-8b7a-6e5f4d3c2b1a",
                  "project_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                  "folder_id": null,
                  "name": "Competitor digest",
                  "kind": "page",
                  "ext": "html",
                  "size_bytes": 1834,
                  "status": "ready",
                  "grounding": true,
                  "created_at": "2026-10-03T06:01:12.000Z",
                  "updated_at": "2026-10-03T06:01:12.000Z",
                  "body": {
                    "format": "html",
                    "content": "<h1>Competitor digest</h1>\n<p>Three launches this week.</p>\n"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the content:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No item with that id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contacts": {
      "get": {
        "operationId": "listContacts",
        "tags": [
          "Contacts"
        ],
        "summary": "List contacts",
        "description": "The account's contacts in Thrive, newest first. Deleted (archived) contacts are left out. Filters combine: `q` searches names, emails, titles and company names; `email` finds the one contact with that address; `updated_since` returns only contacts changed at or after a time, for keeping another system in step.",
        "x-scope": "contacts:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Contains this text, ignoring case (1 to 100 characters).",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100
            }
          },
          {
            "name": "email",
            "in": "query",
            "required": false,
            "description": "Exactly this email address, ignoring case.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 254
            }
          },
          {
            "name": "lifecycle",
            "in": "query",
            "required": false,
            "description": "Only contacts at this lifecycle stage.",
            "schema": {
              "type": "string",
              "enum": [
                "subscriber",
                "lead",
                "marketing_qualified",
                "sales_qualified",
                "opportunity",
                "customer",
                "evangelist",
                "other"
              ]
            }
          },
          {
            "name": "updated_since",
            "in": "query",
            "required": false,
            "description": "Only contacts changed at or after this time: a date (YYYY-MM-DD, UTC) or an ISO-8601 date and time with a zone.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of contacts.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactList"
                },
                "example": {
                  "data": [
                    {
                      "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                      "full_name": "Maya Lindqvist",
                      "first_name": "Maya",
                      "last_name": "Lindqvist",
                      "title": "Head of Operations",
                      "email": "maya@northwind.example",
                      "phone": "+1 415 555 0134",
                      "company_id": "6e5d4c3b-2a1f-4e0d-9c8b-7a6f5e4d3c2b",
                      "status": "active",
                      "lifecycle_stage": "sales_qualified",
                      "lead_source": "Website form",
                      "email_opt_out": false,
                      "created_at": "2026-09-28T16:42:10.000Z",
                      "updated_at": "2026-10-03T09:15:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "invalid_parameter",
                    "message": "lifecycle must be one of: subscriber, lead, marketing_qualified, sales_qualified, opportunity, customer, evangelist, other.",
                    "param": "lifecycle",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the contacts:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No endpoint at that path.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "route_not_found",
                    "message": "There is no endpoint at GET /v1/contact.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "createContact",
        "tags": [
          "Contacts"
        ],
        "summary": "Create a contact",
        "description": "Adds a contact to Thrive and answers `201` with it. It needs a name: `full_name`, or `first_name` and `last_name`. It is unassigned (no owner) until someone in Thrive assigns it, and its history says it was created through the API.\n\n**One contact per email address.** If a contact in the account already has the address, nothing is created or changed: the answer is `409 contact_exists` with that contact's id in `resource_id`, so you can update it with PATCH. An address that belongs to someone erased from Thrive at their request is refused with `409 contact_do_not_contact`.\n\n`Idempotency-Key` is optional and recommended: a retry with the same key and the same request returns the first answer, with `Idempotent-Replay: true`, and never makes a second contact.",
        "x-scope": "contacts:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyOptional"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The contact.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateContactRequest"
              },
              "example": {
                "first_name": "Maya",
                "last_name": "Lindqvist",
                "email": "maya@northwind.example",
                "title": "Head of Operations",
                "lead_source": "Website form"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The contact was created.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Idempotent-Replay": {
                "$ref": "#/components/headers/Idempotent-Replay"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                },
                "example": {
                  "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                  "full_name": "Maya Lindqvist",
                  "first_name": "Maya",
                  "last_name": "Lindqvist",
                  "title": "Head of Operations",
                  "email": "maya@northwind.example",
                  "phone": "+1 415 555 0134",
                  "company_id": "6e5d4c3b-2a1f-4e0d-9c8b-7a6f5e4d3c2b",
                  "status": "active",
                  "lifecycle_stage": "sales_qualified",
                  "lead_source": "Website form",
                  "email_opt_out": false,
                  "created_at": "2026-09-28T16:42:10.000Z",
                  "updated_at": "2026-10-03T09:15:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "missing_field",
                    "message": "A contact needs a name: full_name, or first_name and last_name.",
                    "param": "full_name",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the contacts:write scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No company with that id.",
                    "param": "company_id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "409": {
            "description": "A contact already has that email address, or the Idempotency-Key is busy.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "conflict",
                    "code": "contact_exists",
                    "message": "A contact with that email address already exists. Update it with PATCH /v1/contacts/1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f.",
                    "param": "email",
                    "resource_id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "422": {
            "description": "A body field cannot be used.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "unknown_field",
                    "message": "A contact takes full_name, first_name, last_name, title, email, phone, company_id, lifecycle_stage, lead_source, email_opt_out; \"owner\" is not one of them.",
                    "param": "owner",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contacts/{id}": {
      "get": {
        "operationId": "getContact",
        "tags": [
          "Contacts"
        ],
        "summary": "Get a contact",
        "description": "One contact. A deleted (archived) contact is not found.",
        "x-scope": "contacts:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          }
        ],
        "responses": {
          "200": {
            "description": "The contact.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                },
                "example": {
                  "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                  "full_name": "Maya Lindqvist",
                  "first_name": "Maya",
                  "last_name": "Lindqvist",
                  "title": "Head of Operations",
                  "email": "maya@northwind.example",
                  "phone": "+1 415 555 0134",
                  "company_id": "6e5d4c3b-2a1f-4e0d-9c8b-7a6f5e4d3c2b",
                  "status": "active",
                  "lifecycle_stage": "sales_qualified",
                  "lead_source": "Website form",
                  "email_opt_out": false,
                  "created_at": "2026-09-28T16:42:10.000Z",
                  "updated_at": "2026-10-03T09:15:00.000Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the contacts:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No contact with that id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "operationId": "updateContact",
        "tags": [
          "Contacts"
        ],
        "summary": "Update a contact",
        "description": "Changes only the fields you send; every other field is left as it is. Send `null` to clear a field (not `full_name`, which a contact always has). Changing `first_name` or `last_name` does not change `full_name`: send it too if it should change. An empty body changes nothing and returns the contact.\n\nThe API cannot lift an email opt-out (`email_opt_out: false` on a contact that has opted out is `409 opt_out_locked`), and cannot change someone erased from Thrive at their request (`409 contact_do_not_contact`). Moving a contact to an email address another contact has is `409 contact_exists`.",
        "x-scope": "contacts:write",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The fields to change.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateContactRequest"
              },
              "example": {
                "lifecycle_stage": "customer",
                "title": "COO",
                "phone": null
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The contact, as it is now.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                },
                "example": {
                  "id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                  "full_name": "Maya Lindqvist",
                  "first_name": "Maya",
                  "last_name": "Lindqvist",
                  "title": "COO",
                  "email": "maya@northwind.example",
                  "phone": null,
                  "company_id": "6e5d4c3b-2a1f-4e0d-9c8b-7a6f5e4d3c2b",
                  "status": "active",
                  "lifecycle_stage": "customer",
                  "lead_source": "Website form",
                  "email_opt_out": false,
                  "created_at": "2026-09-28T16:42:10.000Z",
                  "updated_at": "2026-10-03T09:15:00.000Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the contacts:write scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No contact with that id.",
                    "param": "id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The change is not allowed for this contact.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "conflict",
                    "code": "opt_out_locked",
                    "message": "This contact has opted out of email. The API cannot opt them back in; a person can, in Thrive.",
                    "param": "email_opt_out",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "422": {
            "description": "A body field cannot be used.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "invalid_field",
                    "message": "Lifecycle stage must be one of: subscriber, lead, marketing_qualified, sales_qualified, opportunity, customer, evangelist, other.",
                    "param": "lifecycle_stage",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/skunkworks/projects": {
      "get": {
        "operationId": "listSkunkworksProjects",
        "tags": [
          "Skunk Works"
        ],
        "summary": "List Skunk Works projects",
        "description": "The account's projects that have opened Skunk Works, the most recently opened first, each with its key prefix (and any prefixes it had before), how many items and epics it holds, how many are still open, and its running sprint. A project that has never opened Skunk Works is not listed, and listing does not open it. Deleted projects are left out.",
        "x-scope": "skunkworks:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of projects.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkunkworksProjectList"
                },
                "example": {
                  "data": [
                    {
                      "project_id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                      "name": "Website Redesign",
                      "key_prefix": "WR",
                      "old_prefixes": [],
                      "items": 42,
                      "open_items": 17,
                      "epics": 3,
                      "open_sprints": 2,
                      "active_sprint_id": "2b3c4d5e-6f70-4812-93a4-b5c6d7e8f901",
                      "opened_at": "2026-09-14T10:02:33.120Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the skunkworks:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/skunkworks/projects/{project_id}/items": {
      "get": {
        "operationId": "listSkunkworksItems",
        "tags": [
          "Skunk Works"
        ],
        "summary": "List items",
        "description": "A project's work items, epics included, most recently changed first. Filters combine: `status`, `type`, `sprint_id` (one sprint, from List sprints), `assignee_email` (the person's sign-in email, any case), `epic_key` (the items in one epic), `label`, `q` (an item key, or words in the title or description) and `changed_since` (only items changed at or after a time, for keeping another system in step). A value that is not one of the listed ones is refused with `400 invalid_parameter`, never ignored.",
        "x-scope": "skunkworks:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/SkunkworksProjectId"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only items with this status.",
            "schema": {
              "type": "string",
              "enum": [
                "todo",
                "in_progress",
                "in_review",
                "done"
              ]
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Only items of this type.",
            "schema": {
              "type": "string",
              "enum": [
                "story",
                "task",
                "bug",
                "epic"
              ]
            }
          },
          {
            "name": "sprint_id",
            "in": "query",
            "required": false,
            "description": "Only the items in this sprint.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "assignee_email",
            "in": "query",
            "required": false,
            "description": "Only the items assigned to the person with this sign-in email.",
            "schema": {
              "type": "string",
              "format": "email"
            }
          },
          {
            "name": "epic_key",
            "in": "query",
            "required": false,
            "description": "Only the items in this epic, by its key.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "label",
            "in": "query",
            "required": false,
            "description": "Only the items with this label.",
            "schema": {
              "type": "string",
              "maxLength": 24
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "An item key, or words in the title or description.",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "name": "changed_since",
            "in": "query",
            "required": false,
            "description": "Only items changed at or after this time: a date (YYYY-MM-DD, UTC) or an ISO-8601 date and time with a zone.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of items.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkunkworksItemList"
                },
                "example": {
                  "data": [
                    {
                      "key": "WR-12",
                      "title": "Pricing page: annual toggle",
                      "type": "story",
                      "status": "in_progress",
                      "priority": "high",
                      "points": 5,
                      "due_date": "2026-10-16",
                      "labels": [
                        "frontend"
                      ],
                      "epic_key": "WR-3",
                      "sprint_id": "2b3c4d5e-6f70-4812-93a4-b5c6d7e8f901",
                      "sprint_name": "Sprint 4",
                      "assignee_email": "sam@acme.example",
                      "assignee_name": "Sam Ortiz",
                      "linked_documents": 1,
                      "created_at": "2026-10-01T09:12:40.512Z",
                      "updated_at": "2026-10-06T15:30:02.004Z",
                      "closed_at": null
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "invalid_parameter",
                    "message": "status must be one of: todo, in_progress, in_review, done.",
                    "param": "status",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the skunkworks:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No Skunk Works project with that id.",
                    "param": "project_id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/skunkworks/projects/{project_id}/items/{key}": {
      "get": {
        "operationId": "getSkunkworksItem",
        "tags": [
          "Skunk Works"
        ],
        "summary": "Get an item",
        "description": "One work item by its key: everything in the list, its description as plain text and as the HTML the hub shows, the Data Management documents linked to it (by their current names, and whether each is archived), and its whole history, oldest first: created, every change of status, sprint, assignee and the other fields, documents linked, and every comment with its text. Images in the description are references to the item's own images; the API hands out no image files.",
        "x-scope": "skunkworks:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/SkunkworksProjectId"
          },
          {
            "$ref": "#/components/parameters/SkunkworksItemKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The item.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkunkworksItemDetail"
                },
                "example": {
                  "key": "WR-12",
                  "title": "Pricing page: annual toggle",
                  "type": "story",
                  "status": "in_progress",
                  "priority": "high",
                  "points": 5,
                  "due_date": "2026-10-16",
                  "labels": [
                    "frontend"
                  ],
                  "epic_key": "WR-3",
                  "sprint_id": "2b3c4d5e-6f70-4812-93a4-b5c6d7e8f901",
                  "sprint_name": "Sprint 4",
                  "assignee_email": "sam@acme.example",
                  "assignee_name": "Sam Ortiz",
                  "linked_documents": 1,
                  "created_at": "2026-10-01T09:12:40.512Z",
                  "updated_at": "2026-10-06T15:30:02.004Z",
                  "closed_at": null,
                  "description_text": "Add a monthly / annual toggle above the plan cards. Annual shows the yearly price and the saving.",
                  "description_html": "<p>Add a monthly / annual toggle above the plan cards. Annual shows the yearly price and the saving.</p>",
                  "documents": [
                    {
                      "id": "5d4c3b2a-1f0e-4d9c-8b7a-6e5f4d3c2b1a",
                      "name": "Pricing research",
                      "archived": false
                    }
                  ],
                  "history": [
                    {
                      "id": "a1b2c3d4-e5f6-4071-8293-a4b5c6d7e8f9",
                      "at": "2026-10-01T09:12:40.512Z",
                      "kind": "created",
                      "actor_email": "lee@acme.example",
                      "from": null,
                      "to": "todo",
                      "text": null
                    },
                    {
                      "id": "b2c3d4e5-f607-4182-93a4-b5c6d7e8f9a0",
                      "at": "2026-10-02T08:00:11.300Z",
                      "kind": "sprint",
                      "actor_email": "lee@acme.example",
                      "from": "Backlog",
                      "to": "Sprint 4",
                      "text": null
                    },
                    {
                      "id": "c3d4e5f6-0718-4293-a4b5-c6d7e8f9a0b1",
                      "at": "2026-10-06T15:30:02.004Z",
                      "kind": "comment",
                      "actor_email": "sam@acme.example",
                      "from": null,
                      "to": null,
                      "text": "Toggle is in; the saving badge is next."
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "invalid_id",
                    "message": "That is not an item key, such as WR-12.",
                    "param": "key",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the skunkworks:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No item with that key in this project.",
                    "param": "key",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/skunkworks/projects/{project_id}/sprints": {
      "get": {
        "operationId": "listSkunkworksSprints",
        "tags": [
          "Skunk Works"
        ],
        "summary": "List sprints",
        "description": "A project's sprints, newest first: planned, running (`active`) and finished (`completed`). A sprint that was removed before it started is left out. Each carries its dates, its goal, and what is in it now: the number of items and points, and how many of them are done. When a sprint is completed its unfinished items move on, so a completed sprint's totals are the work it finished. A completed sprint also carries its review, as the team saved it.",
        "x-scope": "skunkworks:read",
        "parameters": [
          {
            "$ref": "#/components/parameters/SkunkworksProjectId"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "description": "Only sprints in this state.",
            "schema": {
              "type": "string",
              "enum": [
                "planned",
                "active",
                "completed"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of sprints.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkunkworksSprintList"
                },
                "example": {
                  "data": [
                    {
                      "id": "2b3c4d5e-6f70-4812-93a4-b5c6d7e8f901",
                      "name": "Sprint 4",
                      "goal": "Ship the new pricing page.",
                      "state": "active",
                      "starts_on": "2026-10-05",
                      "ends_on": "2026-10-16",
                      "items": 9,
                      "items_done": 3,
                      "points": 31,
                      "points_done": 11,
                      "review": null,
                      "started_at": "2026-10-05T08:30:00.000Z",
                      "completed_at": null,
                      "created_at": "2026-09-29T14:00:00.000Z"
                    },
                    {
                      "id": "3c4d5e6f-7081-4923-a4b5-c6d7e8f9a012",
                      "name": "Sprint 3",
                      "goal": "Navigation and footer.",
                      "state": "completed",
                      "starts_on": "2026-09-21",
                      "ends_on": "2026-10-02",
                      "items": 8,
                      "items_done": 8,
                      "points": 26,
                      "points_done": 26,
                      "review": "Finished the navigation and the footer; the search box moved to Sprint 4.",
                      "started_at": "2026-09-21T08:30:00.000Z",
                      "completed_at": "2026-10-02T16:45:10.000Z",
                      "created_at": "2026-09-15T11:20:00.000Z"
                    }
                  ],
                  "has_more": false,
                  "next_cursor": null
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request",
                    "code": "invalid_parameter",
                    "message": "state must be one of: planned, active, completed.",
                    "param": "state",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key may not do this.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "permission",
                    "code": "insufficient_scope",
                    "message": "This API key does not have the skunkworks:read scope.",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found in this account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "not_found",
                    "code": "resource_not_found",
                    "message": "No Skunk Works project with that id.",
                    "param": "project_id",
                    "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "vg_live_…",
        "description": "An API key from Account → API keys, sent as `Authorization: Bearer vg_live_…`."
      }
    },
    "headers": {
      "X-Request-Id": {
        "description": "Identifies this request. Quote it to support.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit-Limit": {
        "description": "Requests this key may make in the current minute.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left in the current minute.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the minute ends and the count starts again.",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before trying again.",
        "schema": {
          "type": "integer"
        }
      },
      "Idempotent-Replay": {
        "description": "`true` when this is the stored answer to an earlier request with the same `Idempotency-Key`.",
        "schema": {
          "type": "string"
        }
      }
    },
    "parameters": {
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "How many to return.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        }
      },
      "WorkflowId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The workflow's id.",
        "example": "3f6c1d2e-8a4b-4f7e-9c21-5d0e7a1b2c3d",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "RunId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The run's id.",
        "example": "4b7e1c2d-3f5a-4b6c-8d7e-9f0a1b2c3d4e",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "A unique value for this run, 8 to 64 printable characters (a UUID is ideal). Send the same value again to retry safely.",
        "example": "6f1d2c3b-run-2026-10-03-1420",
        "schema": {
          "type": "string"
        }
      },
      "IdempotencyKeyOptional": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Optional: a unique value for this request, 8 to 64 printable characters (a UUID is ideal). Send the same value again to retry without creating a second one.",
        "example": "6f1d2c3b-page-2026-10-03-1420",
        "schema": {
          "type": "string"
        }
      },
      "IdempotencyKeyEnroll": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "A unique value for this request, 8 to 64 printable characters (a UUID is ideal). Send the same value again to retry without enrolling anyone twice.",
        "example": "6f1d2c3b-enroll-2026-10-07-1642",
        "schema": {
          "type": "string"
        }
      },
      "AutomationId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The automation's id.",
        "example": "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "AutomationRunId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The run's id, from List an automation's runs or Enroll contacts.",
        "example": "7b2c3d4e-5f60-4b7c-9d8e-0f1a2b3c4d5e",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ProjectId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The project's id.",
        "example": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ItemId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The item's id.",
        "example": "5d4c3b2a-1f0e-4d9c-8b7a-6e5f4d3c2b1a",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ContactId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The contact's id.",
        "example": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "SkunkworksProjectId": {
        "name": "project_id",
        "in": "path",
        "required": true,
        "description": "The project's id, from List Skunk Works projects.",
        "example": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "SkunkworksItemKey": {
        "name": "key",
        "in": "path",
        "required": true,
        "description": "The item's key, such as `WR-12`. A key with one of the project's old prefixes, or the number alone, finds it too.",
        "example": "WR-12",
        "schema": {
          "type": "string"
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "The `next_cursor` from the previous page. Leave it out for the first page.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "type",
              "code",
              "message",
              "request_id"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "authentication",
                  "insufficient_credit",
                  "permission",
                  "not_found",
                  "conflict",
                  "rate_limit",
                  "api_error"
                ]
              },
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "param": {
                "type": "string"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "description": "On contact_exists: the id of the contact that already has the address."
              },
              "request_id": {
                "type": "string"
              }
            }
          }
        }
      },
      "RunSummary": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "status",
          "at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "description": "queued, running, succeeded, failed, awaiting_approval or cancelled."
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "When the run was created (UTC)."
          }
        }
      },
      "Workflow": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "description",
          "status",
          "project_id",
          "updated_at",
          "last_run"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "paused"
            ]
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "null for a workflow not yet in a project."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_run": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/RunSummary"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "StartRunRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {},
        "description": "No fields in v1. Any field is refused with 422 unknown_field."
      },
      "RunStarted": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "workflow_id",
          "status",
          "trigger",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "workflow_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued"
            ]
          },
          "trigger": {
            "type": "string",
            "enum": [
              "api"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RunListItem": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "workflow_id",
          "status",
          "trigger",
          "summary",
          "created_at",
          "started_at",
          "finished_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "workflow_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "description": "queued, running, succeeded, failed, partial, awaiting_approval, rejected or cancelled."
          },
          "trigger": {
            "type": "string",
            "description": "How it was started: api, manual (from the hub), schedule or chat."
          },
          "summary": {
            "type": "string",
            "description": "One line on how it went."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "null until the run ends."
          }
        }
      },
      "Run": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "workflow_id",
          "status",
          "trigger",
          "summary",
          "created_at",
          "started_at",
          "finished_at",
          "billed_micros",
          "steps"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "workflow_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "description": "queued, running, succeeded, failed, partial, awaiting_approval, rejected or cancelled."
          },
          "trigger": {
            "type": "string",
            "description": "How it was started: api, manual (from the hub), schedule or chat."
          },
          "summary": {
            "type": "string",
            "description": "One line on how it went."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "null until the run ends."
          },
          "billed_micros": {
            "type": "integer",
            "minimum": 0,
            "description": "What the run has been billed so far, in micros (1,000,000 = US$1)."
          },
          "steps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RunStep"
            },
            "description": "One per block, in the order they ran."
          }
        }
      },
      "RunStep": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "node_id",
          "node_kind",
          "status",
          "ms",
          "error"
        ],
        "properties": {
          "node_id": {
            "type": "string",
            "description": "The block's id in the workflow."
          },
          "node_kind": {
            "type": "string",
            "description": "What kind of block it is: its type in the workflow, such as `source.web`, `agent` or `output.project`."
          },
          "status": {
            "type": "string",
            "description": "queued, running, succeeded, failed, skipped or awaiting."
          },
          "ms": {
            "type": "integer",
            "minimum": 0,
            "description": "How long the block took."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the block failed, or null."
          }
        }
      },
      "RunList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RunListItem"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Usage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "from",
          "to",
          "currency",
          "total_micros",
          "total_usd",
          "balance_micros",
          "balance_usd",
          "by_day",
          "by_feature"
        ],
        "properties": {
          "from": {
            "type": "string",
            "format": "date"
          },
          "to": {
            "type": "string",
            "format": "date"
          },
          "currency": {
            "type": "string",
            "enum": [
              "usd"
            ]
          },
          "total_micros": {
            "type": "integer",
            "minimum": 0,
            "description": "Billed in the window, in micros."
          },
          "total_usd": {
            "type": "string",
            "description": "The same, in US dollars, rounded to the cent."
          },
          "balance_micros": {
            "type": "integer",
            "description": "The account's prepaid balance now, in micros."
          },
          "balance_usd": {
            "type": "string"
          },
          "by_day": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "day",
                "micros"
              ],
              "properties": {
                "day": {
                  "type": "string",
                  "format": "date"
                },
                "micros": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "by_feature": {
            "type": "array",
            "description": "Largest first. `feature` is the name Observability shows each line under.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "feature",
                "micros"
              ],
              "properties": {
                "feature": {
                  "type": "string"
                },
                "micros": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          }
        }
      },
      "Project": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "description",
          "archived",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "archived": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProjectList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Project"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ItemListItem": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "project_id",
          "folder_id",
          "name",
          "kind",
          "ext",
          "size_bytes",
          "status",
          "grounding",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "folder_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The folder it is in, or null at the top of the project."
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "description": "page, file, dataset, image or link."
          },
          "ext": {
            "type": [
              "string",
              "null"
            ],
            "description": "The file extension, such as pdf or csv; html or md for a page."
          },
          "size_bytes": {
            "type": "integer",
            "minimum": 0
          },
          "status": {
            "type": "string",
            "description": "ready, queued or processing while a file is being read, or failed."
          },
          "grounding": {
            "type": "boolean",
            "description": "Whether Chat & Research and Odin use it when answering."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Item": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "project_id",
          "folder_id",
          "name",
          "kind",
          "ext",
          "size_bytes",
          "status",
          "grounding",
          "created_at",
          "updated_at",
          "body"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "folder_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The folder it is in, or null at the top of the project."
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "description": "page, file, dataset, image or link."
          },
          "ext": {
            "type": [
              "string",
              "null"
            ],
            "description": "The file extension, such as pdf or csv; html or md for a page."
          },
          "size_bytes": {
            "type": "integer",
            "minimum": 0
          },
          "status": {
            "type": "string",
            "description": "ready, queued or processing while a file is being read, or failed."
          },
          "grounding": {
            "type": "boolean",
            "description": "Whether Chat & Research and Odin use it when answering."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "body": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ItemBody"
              },
              {
                "type": "null"
              }
            ],
            "description": "A page's text; null for anything that is not a page."
          }
        }
      },
      "ItemBody": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "format",
          "content"
        ],
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "html",
              "markdown"
            ]
          },
          "content": {
            "type": "string"
          }
        }
      },
      "ItemList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ItemListItem"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "CreatePageRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "format",
          "content"
        ],
        "description": "Any other field is refused with 422 unknown_field.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300,
            "description": "The page's name in Data Management."
          },
          "format": {
            "type": "string",
            "enum": [
              "markdown",
              "html"
            ],
            "description": "What `content` is written in."
          },
          "content": {
            "type": "string",
            "description": "The page's text. At most about 900 KB once converted to HTML (and the whole request at most 1 MB)."
          },
          "folder_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "A folder in this project. Leave it out, or null, for the top of the project."
          }
        }
      },
      "Contact": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "full_name",
          "first_name",
          "last_name",
          "title",
          "email",
          "phone",
          "company_id",
          "status",
          "lifecycle_stage",
          "lead_source",
          "email_opt_out",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "full_name": {
            "type": "string"
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "company_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "dormant",
              "do_not_contact"
            ],
            "description": "`do_not_contact`: never contact them. Set in Thrive only; the API cannot change it."
          },
          "lifecycle_stage": {
            "type": [
              "string",
              "null"
            ]
          },
          "lead_source": {
            "type": [
              "string",
              "null"
            ]
          },
          "email_opt_out": {
            "type": "boolean",
            "description": "true when they have opted out of email."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ContactList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Contact"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "CreateContactRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "full_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200,
            "description": "The name shown in Thrive. On create, leave it out to make it from first_name and last_name."
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "First name."
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Last name."
          },
          "title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200,
            "description": "Job title."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "maxLength": 254,
            "description": "Their email address. One contact per address in an account."
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 60,
            "description": "Phone number."
          },
          "company_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "A company in Thrive in this account."
          },
          "lifecycle_stage": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "subscriber",
              "lead",
              "marketing_qualified",
              "sales_qualified",
              "opportunity",
              "customer",
              "evangelist",
              "other",
              null
            ],
            "description": "Where they are in the funnel."
          },
          "lead_source": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80,
            "description": "Where they came from, in your words (at most 80 characters)."
          },
          "email_opt_out": {
            "type": "boolean",
            "description": "true when they have opted out of email. The API can set it to true but cannot set it back to false on a contact that has opted out."
          }
        },
        "description": "Needs full_name, or first_name and last_name. Any other field is refused with 422 unknown_field."
      },
      "UpdateContactRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "full_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200,
            "description": "The name shown in Thrive. On create, leave it out to make it from first_name and last_name."
          },
          "first_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "First name."
          },
          "last_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100,
            "description": "Last name."
          },
          "title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200,
            "description": "Job title."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email",
            "maxLength": 254,
            "description": "Their email address. One contact per address in an account."
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 60,
            "description": "Phone number."
          },
          "company_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "A company in Thrive in this account."
          },
          "lifecycle_stage": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "subscriber",
              "lead",
              "marketing_qualified",
              "sales_qualified",
              "opportunity",
              "customer",
              "evangelist",
              "other",
              null
            ],
            "description": "Where they are in the funnel."
          },
          "lead_source": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80,
            "description": "Where they came from, in your words (at most 80 characters)."
          },
          "email_opt_out": {
            "type": "boolean",
            "description": "true when they have opted out of email. The API can set it to true but cannot set it back to false on a contact that has opted out."
          }
        },
        "description": "Only the fields sent change; null clears one. Any other field is refused with 422 unknown_field."
      },
      "SkunkworksProject": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "project_id",
          "name",
          "key_prefix",
          "old_prefixes",
          "items",
          "open_items",
          "epics",
          "open_sprints",
          "active_sprint_id",
          "opened_at"
        ],
        "properties": {
          "project_id": {
            "type": "string",
            "format": "uuid",
            "description": "The project's id (the same id as in List projects)."
          },
          "name": {
            "type": "string",
            "description": "The project's name."
          },
          "key_prefix": {
            "type": "string",
            "description": "The prefix of its item keys, such as `WR` in `WR-12`."
          },
          "old_prefixes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Prefixes it had before. Keys with them still find its items."
          },
          "items": {
            "type": "integer",
            "minimum": 0,
            "description": "Its work items, epics not counted."
          },
          "open_items": {
            "type": "integer",
            "minimum": 0,
            "description": "Those not yet done."
          },
          "epics": {
            "type": "integer",
            "minimum": 0
          },
          "open_sprints": {
            "type": "integer",
            "minimum": 0,
            "description": "Sprints planned or running."
          },
          "active_sprint_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The running sprint, or null."
          },
          "opened_at": {
            "type": "string",
            "format": "date-time",
            "description": "When Skunk Works was first opened in the project."
          }
        }
      },
      "SkunkworksProjectList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SkunkworksProject"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "SkunkworksItem": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "key",
          "title",
          "type",
          "status",
          "priority",
          "points",
          "due_date",
          "labels",
          "epic_key",
          "sprint_id",
          "sprint_name",
          "assignee_email",
          "assignee_name",
          "linked_documents",
          "created_at",
          "updated_at",
          "closed_at"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "The item's key, such as `WR-12`. It does not change unless the project's prefix is renamed (and the old key still finds it)."
          },
          "title": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "story",
              "task",
              "bug",
              "epic"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "todo",
              "in_progress",
              "in_review",
              "done"
            ],
            "description": "To Do, In Progress, In Review or Done."
          },
          "priority": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "points": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Its estimate in points, or null."
          },
          "due_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "epic_key": {
            "type": [
              "string",
              "null"
            ],
            "description": "The epic it belongs to, by key."
          },
          "sprint_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The sprint it is in, or null in the backlog."
          },
          "sprint_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "assignee_email": {
            "type": [
              "string",
              "null"
            ],
            "description": "The sign-in email of the person it is assigned to."
          },
          "assignee_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "linked_documents": {
            "type": "integer",
            "minimum": 0,
            "description": "How many Data Management documents are linked to it, from its description, its comments or by hand."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When anything about it last changed. Lists are ordered by it, newest first."
          },
          "closed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was done, or null."
          }
        }
      },
      "SkunkworksItemList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SkunkworksItem"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "SkunkworksDocument": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "archived"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The document's id in Data Management (Get an item, with `content:read`)."
          },
          "name": {
            "type": "string",
            "description": "Its name now."
          },
          "archived": {
            "type": "boolean",
            "description": "`true` when it has been archived in Data Management."
          }
        }
      },
      "SkunkworksEvent": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "at",
          "kind",
          "actor_email",
          "from",
          "to",
          "text"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "kind": {
            "type": "string",
            "description": "What happened, such as `created`, `status`, `sprint`, `assignee`, `priority`, `points`, `due`, `epic`, `labels`, `title`, `description`, `comment`, `mention`, `doclink`, `docunlink`, `attachment`, `resolution` or `reopened`. More kinds may be added."
          },
          "actor_email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who did it: their sign-in email. null when the hub did it itself."
          },
          "from": {
            "type": [
              "string",
              "null"
            ],
            "description": "For a change, the value before, as the hub shows it (a status, a sprint's name or Backlog, an email or Unassigned)."
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "description": "For a change, the value after; for a mention, the email of the person mentioned; for a document, its name."
          },
          "text": {
            "type": [
              "string",
              "null"
            ],
            "description": "For a comment, its text."
          }
        }
      },
      "SkunkworksItemDetail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "key",
          "title",
          "type",
          "status",
          "priority",
          "points",
          "due_date",
          "labels",
          "epic_key",
          "sprint_id",
          "sprint_name",
          "assignee_email",
          "assignee_name",
          "linked_documents",
          "created_at",
          "updated_at",
          "closed_at",
          "description_text",
          "description_html",
          "documents",
          "history"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "The item's key, such as `WR-12`. It does not change unless the project's prefix is renamed (and the old key still finds it)."
          },
          "title": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "story",
              "task",
              "bug",
              "epic"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "todo",
              "in_progress",
              "in_review",
              "done"
            ],
            "description": "To Do, In Progress, In Review or Done."
          },
          "priority": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "points": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Its estimate in points, or null."
          },
          "due_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "epic_key": {
            "type": [
              "string",
              "null"
            ],
            "description": "The epic it belongs to, by key."
          },
          "sprint_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The sprint it is in, or null in the backlog."
          },
          "sprint_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "assignee_email": {
            "type": [
              "string",
              "null"
            ],
            "description": "The sign-in email of the person it is assigned to."
          },
          "assignee_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "linked_documents": {
            "type": "integer",
            "minimum": 0,
            "description": "How many Data Management documents are linked to it, from its description, its comments or by hand."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When anything about it last changed. Lists are ordered by it, newest first."
          },
          "closed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was done, or null."
          },
          "description_text": {
            "type": "string",
            "description": "The description as plain text."
          },
          "description_html": {
            "type": "string",
            "description": "The description as the hub shows it: formatting, links, document and item chips, and references to the item's images."
          },
          "documents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SkunkworksDocument"
            },
            "description": "The Data Management documents linked to it."
          },
          "history": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SkunkworksEvent"
            },
            "description": "Everything that happened to it, oldest first, comments included."
          }
        }
      },
      "SkunkworksSprint": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "goal",
          "state",
          "starts_on",
          "ends_on",
          "items",
          "items_done",
          "points",
          "points_done",
          "review",
          "started_at",
          "completed_at",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "goal": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "planned",
              "active",
              "completed"
            ],
            "description": "`planned`, `active` (running) or `completed`."
          },
          "starts_on": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "ends_on": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "items": {
            "type": "integer",
            "minimum": 0,
            "description": "The items in it now."
          },
          "items_done": {
            "type": "integer",
            "minimum": 0
          },
          "points": {
            "type": "integer",
            "minimum": 0,
            "description": "Their points added up."
          },
          "points_done": {
            "type": "integer",
            "minimum": 0,
            "description": "The points of the items that are done."
          },
          "review": {
            "type": [
              "string",
              "null"
            ],
            "description": "A completed sprint's review, as the team saved it; null otherwise."
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SkunkworksSprintList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SkunkworksSprint"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Automation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "project_id",
          "name",
          "status",
          "trigger",
          "published_version",
          "has_unpublished_changes",
          "step_count",
          "step_types",
          "sends",
          "enrollments",
          "last_run_at",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid",
            "description": "The project it belongs to."
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published",
              "paused"
            ],
            "description": "`draft` (never published), `published` (running) or `paused`. Only a published automation takes enrollments."
          },
          "trigger": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "type",
              "label"
            ],
            "description": "What starts it, as the hub's list shows it under Starts when.",
            "properties": {
              "type": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The trigger, such as `contact.created`, `deal.stage_changed`, `schedule` or `manual`; null before one is chosen."
              },
              "label": {
                "type": "string",
                "description": "Its name in the hub, such as Contact created."
              }
            }
          },
          "published_version": {
            "type": "integer",
            "minimum": 0,
            "description": "The version that runs; 0 for a draft that was never published."
          },
          "has_unpublished_changes": {
            "type": "boolean",
            "description": "`true` when the draft in the builder differs from the published version."
          },
          "step_count": {
            "type": "integer",
            "minimum": 0,
            "description": "How many steps the version that runs has (the draft's, before it is published), branches included."
          },
          "step_types": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The kinds of step in it, each once, such as `email.send`, `sms.send`, `wait` or `ai.write`. More kinds may be added."
          },
          "sends": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "email",
                "sms",
                "team"
              ]
            },
            "description": "What it sends: `email` and `sms` to the contact, `team` to people in the account."
          },
          "enrollments": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "total",
              "active",
              "completed"
            ],
            "description": "Its runs, test runs left out.",
            "properties": {
              "total": {
                "type": "integer",
                "minimum": 0,
                "description": "Every run, except a delivery its Run only if filter skipped."
              },
              "active": {
                "type": "integer",
                "minimum": 0,
                "description": "Running or waiting now."
              },
              "completed": {
                "type": "integer",
                "minimum": 0
              }
            }
          },
          "last_run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When its latest run started; null if it has never run."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AutomationList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Automation"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "EnrollRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "contact_ids"
        ],
        "properties": {
          "contact_ids": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Thrive contact ids, 1 to 500. A repeated id counts once."
          }
        }
      },
      "EnrollResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "automation_id",
          "enrolled",
          "runs",
          "skipped"
        ],
        "properties": {
          "automation_id": {
            "type": "string",
            "format": "uuid"
          },
          "enrolled": {
            "type": "integer",
            "minimum": 0,
            "description": "How many contacts were enrolled."
          },
          "runs": {
            "type": "array",
            "description": "The run each enrolled contact got.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "id",
                "contact_id"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "description": "The run, for Get an automation run."
                },
                "contact_id": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            }
          },
          "skipped": {
            "type": "array",
            "description": "The contacts not enrolled, each with why.",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "contact_id",
                "reason"
              ],
              "properties": {
                "contact_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "reason": {
                  "type": "string",
                  "enum": [
                    "not_found",
                    "erased",
                    "already_enrolled",
                    "enrolled_before",
                    "too_many_automations"
                  ]
                }
              }
            }
          }
        }
      },
      "AutomationRun": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "automation_id",
          "contact",
          "status",
          "status_label",
          "started_by",
          "test",
          "version",
          "started_at",
          "finished_at",
          "next_at",
          "error"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "automation_id": {
            "type": "string",
            "format": "uuid"
          },
          "contact": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "id",
                  "name"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "name": {
                    "type": "string"
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Who it runs for; null for a run with no contact: a scheduled run, or a store event that is not about a customer (such as Mercato's low stock), which runs for the store with no contact."
          },
          "status": {
            "type": "string",
            "enum": [
              "running",
              "waiting",
              "succeeded",
              "failed",
              "stopped",
              "skipped"
            ],
            "description": "As Run history shows it: `running`, `waiting` (at a wait, until `next_at`), `succeeded`, `failed`, `stopped`, or `skipped` (its Run only if filter did not match)."
          },
          "status_label": {
            "type": "string",
            "description": "The same, in the words Run history uses."
          },
          "started_by": {
            "type": "string",
            "description": "What started it, as Run history's Started by column says: the trigger, the person who enrolled the contact, or the API key."
          },
          "test": {
            "type": "boolean",
            "description": "`true` for a test run from the builder, which sends only to the person testing."
          },
          "version": {
            "type": "integer",
            "minimum": 0,
            "description": "The published version it runs."
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "null until it ends."
          },
          "next_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "For a waiting run, when it carries on; null otherwise."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why it failed or was stopped."
          }
        }
      },
      "AutomationRunList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AutomationRun"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "AutomationRunStep": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "step_id",
          "name",
          "type",
          "status",
          "started_at",
          "ms",
          "error"
        ],
        "properties": {
          "step_id": {
            "type": "string",
            "description": "The step's id in the automation; `t` is the trigger."
          },
          "name": {
            "type": "string",
            "description": "The step's name in the builder."
          },
          "type": {
            "type": "string",
            "description": "What kind of step it is, such as `email.send`, `sms.send`, `wait` or `trigger`."
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "failed",
              "skipped",
              "waiting"
            ]
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "ms": {
            "type": "integer",
            "minimum": 0,
            "description": "How long the step took."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the step failed, or null."
          }
        }
      },
      "AutomationRunDetail": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "automation_id",
          "contact",
          "status",
          "status_label",
          "started_by",
          "test",
          "version",
          "started_at",
          "finished_at",
          "next_at",
          "error",
          "failed_step_id",
          "steps"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "automation_id": {
            "type": "string",
            "format": "uuid"
          },
          "contact": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "id",
                  "name"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "name": {
                    "type": "string"
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Who it runs for; null for a run with no contact: a scheduled run, or a store event that is not about a customer (such as Mercato's low stock), which runs for the store with no contact."
          },
          "status": {
            "type": "string",
            "enum": [
              "running",
              "waiting",
              "succeeded",
              "failed",
              "stopped",
              "skipped"
            ],
            "description": "As Run history shows it: `running`, `waiting` (at a wait, until `next_at`), `succeeded`, `failed`, `stopped`, or `skipped` (its Run only if filter did not match)."
          },
          "status_label": {
            "type": "string",
            "description": "The same, in the words Run history uses."
          },
          "started_by": {
            "type": "string",
            "description": "What started it, as Run history's Started by column says: the trigger, the person who enrolled the contact, or the API key."
          },
          "test": {
            "type": "boolean",
            "description": "`true` for a test run from the builder, which sends only to the person testing."
          },
          "version": {
            "type": "integer",
            "minimum": 0,
            "description": "The published version it runs."
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "null until it ends."
          },
          "next_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "For a waiting run, when it carries on; null otherwise."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why it failed or was stopped."
          },
          "failed_step_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "For a failed run, the step it stopped at."
          },
          "steps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AutomationRunStep"
            },
            "description": "The steps it has run, in order. What a step sent (addresses, text) is not included."
          }
        }
      },
      "WorkflowList": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Workflow"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request is not valid.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "invalid_request",
                "code": "invalid_cursor",
                "message": "cursor is not valid",
                "param": "cursor",
                "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "No valid API key.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "WWW-Authenticate": {
            "description": "Always `Bearer`.",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "authentication",
                "code": "invalid_api_key",
                "message": "The API key is missing or not valid.",
                "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key may not do this.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "permission",
                "code": "insufficient_scope",
                "message": "This API key does not have the workflows:read scope.",
                "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found in this account.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "not_found",
                "code": "resource_not_found",
                "message": "No project with that id.",
                "param": "project_id",
                "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "rate_limit",
                "code": "rate_limited",
                "message": "Too many requests for this API key. Try again in 12 seconds.",
                "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
              }
            }
          }
        }
      },
      "InternalError": {
        "description": "Something went wrong on our side.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "api_error",
                "code": "internal_error",
                "message": "Something went wrong on our end. Please try again.",
                "request_id": "req_5f0c2a9e1b7d4c3a8e6f1d20"
              }
            }
          }
        }
      }
    }
  }
}
