{
  "openapi": "3.1.0",
  "info": {
    "title": "Sortd API",
    "version": "2.0.0",
    "description": "Sortd's public API — the work/operations layer around the mailbox. Email threads become owned,\nstateful work items with assignments, AI classification and customer context.\n\n**Base URL** — `https://api.sortd.com/v2` (same-origin in local/dev).\n\n**Auth** — send an API key as a Bearer token: `Authorization: Bearer sk_live_…`. Create and\nscope keys in the app under Settings → API; the secret is shown once. `GET /me` needs no scopes\nand is the quickest way to confirm a key works.\n\n**Responses** — every success is `{ \"data\": …, \"meta\": { \"request_id\": … } }`; lists add\n`meta.pagination` (`next_cursor`, `has_more`, `limit`). Errors are\n`{ \"error\": { \"code\", \"message\" } }`. **Tolerate unknown fields** — the API evolves additively.\n\n**Writes** — every write (`POST`/`PUT`/`PATCH`/`DELETE`) accepts an optional `Idempotency-Key`\nheader so retries are safe: a repeated request with the same key replays the original response,\nand reusing a key with a different body returns `409`. Each endpoint lists the scopes it requires;\na key missing a scope gets `403`."
  },
  "servers": [
    {
      "url": "https://api.sortd.com/v2",
      "description": "Sortd public API v2. The in-browser \"Try It\" client is routed same-origin by the docs site, so it works without CORS."
    }
  ],
  "tags": [
    {
      "name": "Identity",
      "description": "Who the request is authenticated as, and the key it used."
    },
    {
      "name": "Teams",
      "description": "Teams (organisations) the API key can act within."
    },
    {
      "name": "Boards",
      "description": "Boards — the containers for lists and work items."
    },
    {
      "name": "Lists",
      "description": "Lists within a board."
    },
    {
      "name": "Tasks",
      "description": "Work items: create, read, update, move and complete tasks."
    },
    {
      "name": "AI",
      "description": "Thread classification and cached AI analyses (urgency, sentiment, status)."
    },
    {
      "name": "Webhooks",
      "description": "Signed, retried event delivery with a replayable delivery log."
    },
    {
      "name": "Zapier",
      "description": "Zapier-shaped trigger/action helpers."
    },
    {
      "name": "Companies"
    },
    {
      "name": "Contacts"
    },
    {
      "name": "Custom fields"
    },
    {
      "name": "Email"
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Bearer **API key**. Create one in the app under **Settings → API**, copy the `sk_live_…`\nsecret (shown once), and send it as `Authorization: Bearer sk_live_…`.\n\nIn the \"Try It\" panel, choose this scheme and paste **only** the `sk_…` value — do not type\nthe word `Bearer`. Resource endpoints also require the key owner to have access to the\nteam/board and an active subscription."
      }
    },
    "schemas": {
      "Me": {
        "type": "object",
        "properties": {
          "user_id": {
            "type": "string",
            "description": "Internal user id of the authenticated principal."
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "display_name": {
            "type": "string",
            "nullable": true
          },
          "key": {
            "type": "object",
            "nullable": true,
            "description": "The API key the request authenticated with (absent for legacy JWT auth).",
            "properties": {
              "id": {
                "type": "string"
              },
              "type": {
                "type": "string"
              },
              "scopes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "SubscriptionSummary": {
        "type": "object",
        "nullable": true,
        "description": "The team's Sortd subscription. Null when the team has no subscription record. `paid: false` means tier-gated endpoints will return `subscription_required` for this team.",
        "properties": {
          "plan": {
            "type": "string",
            "nullable": true,
            "description": "Subscription plan name, e.g. \"Pro\"."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "trialing",
              "trial_expired",
              "past_due",
              "unpaid",
              "cancelled",
              "cancel_pending",
              "migration_pending",
              "unknown"
            ]
          },
          "paid": {
            "type": "boolean",
            "description": "True when the subscription is paid (not free) and not cancelled — the same check the API gates on."
          }
        }
      },
      "Team": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Team/organisation id."
          },
          "name": {
            "type": "string"
          },
          "subscription": {
            "$ref": "#/components/schemas/SubscriptionSummary"
          }
        }
      },
      "Board": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque board id (brd_…)."
          },
          "name": {
            "type": "string"
          },
          "board_type": {
            "type": "string",
            "enum": [
              "private",
              "shared",
              "sales"
            ],
            "description": "private = personal, shared = team-visible, sales = sales pipeline."
          },
          "template": {
            "type": "string",
            "nullable": true,
            "description": "The template the board was created from (e.g. sales, hiring, project, support) — a classifier; the column/list names differ per template while board_type stays one of the three. Null if created without a template."
          },
          "team_id": {
            "type": "string",
            "nullable": true,
            "description": "Owning team id, or null for a personal board."
          },
          "subscription": {
            "$ref": "#/components/schemas/SubscriptionSummary"
          }
        }
      },
      "List": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque list id (lst_…)."
          },
          "name": {
            "type": "string"
          },
          "board_id": {
            "type": "string"
          }
        }
      },
      "CustomField": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque custom-field id — pass as `field_id` when setting a value."
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "description": "Field type, e.g. text, number, date, tag, status, select."
          },
          "ai": {
            "type": "boolean",
            "description": "True if the field is managed by Sortd AI (e.g. Response Status) — read-only via the API."
          },
          "display_on_card": {
            "type": "boolean",
            "description": "Whether the field is shown on task cards. Only meaningful for status and tag fields."
          },
          "options": {
            "type": "array",
            "nullable": true,
            "description": "Allowed options for tag/status/select-type fields. Set a value by passing the option id(s).",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Option id — use this as the value for option-type fields."
                },
                "label": {
                  "type": "string",
                  "description": "Human-readable option label."
                }
              }
            }
          }
        }
      },
      "CustomFieldValue": {
        "type": "object",
        "required": [
          "field_id"
        ],
        "properties": {
          "field_id": {
            "type": "string",
            "description": "Custom-field id (from GET /boards/{id}/custom-fields)."
          },
          "value": {
            "description": "The value. For tag/status/select: an array of option id(s). For text/number/date: the raw value. `[]` or null clears it."
          }
        }
      },
      "Task": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque task id (tsk_…)."
          },
          "content_type": {
            "type": "string",
            "enum": [
              "task",
              "sale"
            ],
            "description": "task = normal card; sale = a deal card on a sales board (see deal/contact_id)."
          },
          "title": {
            "type": "string"
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "board_id": {
            "type": "string",
            "nullable": true
          },
          "board_name": {
            "type": "string",
            "nullable": true
          },
          "list_id": {
            "type": "string",
            "nullable": true
          },
          "list_name": {
            "type": "string",
            "nullable": true
          },
          "completed": {
            "type": "boolean"
          },
          "shade": {
            "type": "string",
            "enum": [
              "none",
              "yellow",
              "cyan",
              "light_blue",
              "teal",
              "blue",
              "indigo",
              "yellow_green",
              "lime",
              "green",
              "amber",
              "red",
              "magenta",
              "purple",
              "orange",
              "deep_orange",
              "pink",
              "brown",
              "grey",
              "dark_grey",
              "black"
            ],
            "description": "The task's colour ('none' if unset)."
          },
          "custom_fields": {
            "type": "array",
            "description": "Custom-field values set on this task. For tag/status, `value` is option id(s); resolve labels via GET /boards/{id}/custom-fields.",
            "items": {
              "$ref": "#/components/schemas/CustomFieldValue"
            }
          },
          "due_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "thread_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Raw Gmail thread ids (hex) of every email thread linked to this task — pass to the email endpoints. Empty if none."
          },
          "contact_id": {
            "type": "string",
            "nullable": true,
            "description": "Opaque id (con_…) of the linked CRM contact — sale cards only; null otherwise."
          },
          "deal": {
            "type": "object",
            "nullable": true,
            "description": "Deal details — present on content_type=sale cards only.",
            "properties": {
              "value": {
                "type": "number",
                "nullable": true
              },
              "source": {
                "type": "string",
                "nullable": true
              },
              "close_date": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "loss_reason": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "assignee": {
            "type": "object",
            "nullable": true,
            "properties": {
              "name": {
                "type": "string",
                "nullable": true
              },
              "email": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "first_thread": {
            "type": "object",
            "nullable": true,
            "properties": {
              "title": {
                "type": "string",
                "nullable": true
              },
              "received_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "from": {
                "type": "string",
                "nullable": true
              }
            }
          }
        }
      },
      "CreateTaskRequest": {
        "type": "object",
        "required": [
          "list_id",
          "title"
        ],
        "properties": {
          "list_id": {
            "type": "string",
            "description": "Opaque list id (lst_…) to create the task in."
          },
          "title": {
            "type": "string",
            "description": "Short task title shown on the card."
          },
          "notes": {
            "type": "string",
            "description": "Optional free-text body/notes for the task."
          },
          "due_at": {
            "type": "string",
            "format": "date-time",
            "description": "Optional due date/time, ISO-8601 (e.g. 2026-06-15T09:00:00Z)."
          }
        }
      },
      "MoveTaskRequest": {
        "type": "object",
        "required": [
          "list_id"
        ],
        "properties": {
          "list_id": {
            "type": "string",
            "description": "Destination opaque list id (lst_…)."
          }
        }
      },
      "UpdateTaskRequest": {
        "type": "object",
        "description": "Partial update — send only the fields to change. At least one is required.",
        "properties": {
          "shade": {
            "type": "string",
            "enum": [
              "none",
              "yellow",
              "cyan",
              "light_blue",
              "teal",
              "blue",
              "indigo",
              "yellow_green",
              "lime",
              "green",
              "amber",
              "red",
              "magenta",
              "purple",
              "orange",
              "deep_orange",
              "pink",
              "brown",
              "grey",
              "dark_grey",
              "black"
            ],
            "description": "The task's colour; 'none' clears it."
          },
          "custom_fields": {
            "type": "array",
            "description": "Custom-field values to set. Each needs field_id (from GET /boards/{id}/custom-fields) and value (option id(s) for tag/status; raw value otherwise).",
            "items": {
              "$ref": "#/components/schemas/CustomFieldValue"
            }
          }
        }
      },
      "CreateBoardRequest": {
        "type": "object",
        "required": [
          "team_id",
          "name"
        ],
        "properties": {
          "team_id": {
            "type": "string",
            "description": "Team/organisation id (from /me or /teams) to create the shared board under."
          },
          "name": {
            "type": "string",
            "description": "Board name."
          }
        }
      },
      "UpdateBoardRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "New board name."
          }
        }
      },
      "CreateListRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "List name."
          }
        }
      },
      "UpdateListRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "New list name."
          }
        }
      },
      "CreateCustomFieldRequest": {
        "type": "object",
        "required": [
          "name",
          "type"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Field name."
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "multiline_text",
              "number",
              "date",
              "checkbox",
              "url",
              "phone",
              "select",
              "status",
              "tag",
              "multiselect"
            ],
            "description": "Field type. Option types (select/status/tag/multiselect) accept an `options` list."
          },
          "options": {
            "type": "array",
            "nullable": true,
            "description": "For option-type fields: the selectable options. Each is created with a fresh id.",
            "items": {
              "type": "object",
              "required": [
                "label"
              ],
              "properties": {
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "display_on_card": {
            "type": "boolean",
            "description": "Show this field on task cards. Only valid for `status` and `tag` fields — sending it for any other type returns a validation error."
          }
        }
      },
      "UpdateCustomFieldRequest": {
        "type": "object",
        "description": "Partial update — send only what changes. At least one of name, options or display_on_card is required.",
        "properties": {
          "name": {
            "type": "string",
            "description": "New field name."
          },
          "options": {
            "type": "array",
            "nullable": true,
            "description": "Replaces the field options. Include an option `id` to keep an existing option; omit it to add a new one.",
            "items": {
              "type": "object",
              "required": [
                "label"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "display_on_card": {
            "type": "boolean",
            "description": "Show this field on task cards. Only valid for `status` and `tag` fields — sending it for any other type returns a validation error."
          }
        }
      },
      "EmailAddress": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "EmailMessage": {
        "type": "object",
        "description": "A message on an email thread. Ids are raw Gmail message ids (hex). Bodies are present only when the thread was fetched with include=messages (email.body.read scope).",
        "properties": {
          "id": {
            "type": "string",
            "description": "Raw Gmail message id (hex)."
          },
          "subject": {
            "type": "string",
            "nullable": true
          },
          "from": {
            "$ref": "#/components/schemas/EmailAddress"
          },
          "to": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmailAddress"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmailAddress"
            }
          },
          "date": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "snippet": {
            "type": "string",
            "nullable": true
          },
          "unread": {
            "type": "boolean"
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Raw Gmail label ids on the message."
          },
          "body_text": {
            "type": "string",
            "nullable": true,
            "description": "Plain-text body (include=messages only)."
          },
          "body_html": {
            "type": "string",
            "nullable": true,
            "description": "HTML body (include=messages only)."
          }
        }
      },
      "EmailThread": {
        "type": "object",
        "description": "A thread in the key owner's mailbox. Thread/message ids are the RAW Gmail ids (hex) — pass them to other Gmail tooling as-is.",
        "properties": {
          "provider": {
            "type": "string",
            "description": "Email provider, e.g. gmail."
          },
          "thread_id": {
            "type": "string",
            "description": "Raw Gmail thread id (hex)."
          },
          "message_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "subject": {
            "type": "string",
            "nullable": true
          },
          "participants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmailAddress"
            }
          },
          "latest_message_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "snippet": {
            "type": "string",
            "nullable": true,
            "description": "Snippet of the latest message."
          },
          "direction": {
            "type": "string",
            "nullable": true,
            "enum": [
              "inbound",
              "outbound",
              null
            ],
            "description": "inbound = latest message received, outbound = latest message sent by the mailbox owner."
          },
          "unread": {
            "type": "boolean",
            "description": "True when any message on the thread is unread."
          },
          "linked_tasks": {
            "type": "array",
            "nullable": true,
            "description": "Sortd tasks this thread is attached to (that the key can access). Present on the single-thread GET; omitted from search results — fetch the thread to see its tasks.",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Opaque task id (tsk_…)."
                },
                "title": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          },
          "messages": {
            "type": "array",
            "nullable": true,
            "items": {
              "$ref": "#/components/schemas/EmailMessage"
            },
            "description": "Present only when include=messages."
          }
        }
      },
      "Contact": {
        "type": "object",
        "description": "A CRM contact. Search returns the company-grouped subset; get/create/update return the full record.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque contact id (con_…)."
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Contact display name."
          },
          "first_name": {
            "type": "string",
            "nullable": true
          },
          "last_name": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "nullable": true,
            "description": "Contact type, e.g. sale / general / hire."
          },
          "stage": {
            "type": "string",
            "nullable": true,
            "description": "Pipeline stage, e.g. Lead / Customer."
          },
          "source": {
            "type": "string",
            "nullable": true
          },
          "contact_info": {
            "type": "array",
            "description": "Emails, phones, etc.",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "company": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "nullable": true,
                "description": "Opaque company id (cmp_…)."
              },
              "name": {
                "type": "string",
                "nullable": true
              }
            }
          }
        }
      },
      "Company": {
        "type": "object",
        "description": "A CRM company.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque company id (cmp_…)."
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "nullable": true
          },
          "stage": {
            "type": "string",
            "nullable": true
          },
          "company_info": {
            "type": "array",
            "description": "Website, domain, phone, etc.",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CreateContactRequest": {
        "type": "object",
        "required": [
          "team_id",
          "name"
        ],
        "description": "Create a CRM contact. Link a company via company_id (existing cmp_) or company_name (find-or-create); if neither, the org default company is used.",
        "properties": {
          "team_id": {
            "type": "string",
            "description": "Team/organisation id (required)."
          },
          "name": {
            "type": "string",
            "description": "Display name (required)."
          },
          "first_name": {
            "type": "string",
            "nullable": true
          },
          "last_name": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "nullable": true,
            "description": "Default general."
          },
          "stage": {
            "type": "string",
            "nullable": true
          },
          "source": {
            "type": "string",
            "nullable": true
          },
          "contact_info": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "company_id": {
            "type": "string",
            "nullable": true,
            "description": "Existing company (cmp_…) to link."
          },
          "company_name": {
            "type": "string",
            "nullable": true,
            "description": "Company name to find-or-create (ignored if company_id is given)."
          }
        }
      },
      "UpdateContactRequest": {
        "type": "object",
        "description": "Partial update — provide only the fields to change.",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "first_name": {
            "type": "string",
            "nullable": true
          },
          "last_name": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "nullable": true
          },
          "stage": {
            "type": "string",
            "nullable": true
          },
          "source": {
            "type": "string",
            "nullable": true
          },
          "contact_info": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "tags": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            }
          },
          "company_id": {
            "type": "string",
            "nullable": true,
            "description": "Re-link to a different existing company (cmp_…)."
          }
        }
      },
      "CreateCompanyRequest": {
        "type": "object",
        "required": [
          "team_id"
        ],
        "properties": {
          "team_id": {
            "type": "string",
            "description": "Team/organisation id (required)."
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "nullable": true
          },
          "stage": {
            "type": "string",
            "nullable": true
          },
          "company_info": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "UpdateCompanyRequest": {
        "type": "object",
        "description": "Partial update — provide only the fields to change.",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "nullable": true
          },
          "stage": {
            "type": "string",
            "nullable": true
          },
          "company_info": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "tags": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            }
          }
        }
      },
      "EmailLabel": {
        "type": "object",
        "description": "A Gmail label/folder in the mailbox.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Gmail label id — system labels are fixed (INBOX, SENT, SPAM, TRASH, …); user labels are Label_… "
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Display name."
          },
          "type": {
            "type": "string",
            "enum": [
              "system",
              "user"
            ],
            "description": "system = Gmail built-in (INBOX/SENT/SPAM/TRASH/DRAFT/STARRED/IMPORTANT/CATEGORY_*); user = custom label."
          }
        }
      },
      "EmailLabels": {
        "type": "object",
        "description": "The mailbox's labels.",
        "properties": {
          "labels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmailLabel"
            }
          }
        }
      },
      "Draft": {
        "type": "object",
        "description": "A Gmail draft (D47: drafts live only in Gmail — the user can edit, send or delete it there; there is no approval state). Ids are raw Gmail ids (hex), except draft_id which is Gmail’s opaque draft id.",
        "properties": {
          "draft_id": {
            "type": "string",
            "description": "Gmail draft id — pass to the send-draft endpoint."
          },
          "message_id": {
            "type": "string",
            "nullable": true,
            "description": "Raw Gmail message id (hex) of the draft message."
          },
          "thread_id": {
            "type": "string",
            "nullable": true,
            "description": "Raw Gmail thread id (hex)."
          },
          "subject": {
            "type": "string",
            "nullable": true
          },
          "snippet": {
            "type": "string",
            "nullable": true
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "date_modified": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "CreateDraftRequest": {
        "type": "object",
        "required": [
          "body"
        ],
        "properties": {
          "to": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            },
            "description": "Recipient email addresses (optional — a draft can be recipient-less until the human fills it in)."
          },
          "cc": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            }
          },
          "bcc": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            }
          },
          "subject": {
            "type": "string",
            "nullable": true,
            "description": "Defaults to \"Re: <thread subject>\"."
          },
          "body": {
            "type": "string",
            "description": "The draft body."
          },
          "is_html": {
            "type": "boolean",
            "description": "Whether body is HTML (default true)."
          }
        }
      },
      "CreateNewDraftRequest": {
        "type": "object",
        "required": [
          "to",
          "subject",
          "body"
        ],
        "description": "A brand-new draft (no thread). Unlike the thread-scoped draft, recipients and subject are required.",
        "properties": {
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Recipient email addresses (at least one)."
          },
          "cc": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            }
          },
          "bcc": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            }
          },
          "subject": {
            "type": "string",
            "description": "The draft subject."
          },
          "body": {
            "type": "string",
            "description": "The draft body. May be an empty string (e.g. an attachment-only draft)."
          },
          "is_html": {
            "type": "boolean",
            "description": "Whether body is HTML (default true)."
          }
        }
      },
      "ReplyRequest": {
        "type": "object",
        "required": [
          "body"
        ],
        "description": "Recipients CANNOT be set — they are derived from the thread (D49). Requests containing to/cc/bcc are rejected.",
        "properties": {
          "body": {
            "type": "string",
            "description": "The reply body."
          },
          "is_html": {
            "type": "boolean",
            "description": "Whether body is HTML (default true)."
          },
          "mode": {
            "type": "string",
            "enum": [
              "reply",
              "reply_all"
            ],
            "description": "reply = sender only (honouring Reply-To); reply_all = keep the To/Cc audience. Default reply."
          }
        }
      },
      "SendEmailRequest": {
        "type": "object",
        "required": [
          "to",
          "subject",
          "body"
        ],
        "properties": {
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Recipient email addresses."
          },
          "cc": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            }
          },
          "bcc": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            }
          },
          "subject": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "is_html": {
            "type": "boolean",
            "description": "Whether body is HTML (default true)."
          },
          "thread_id": {
            "type": "string",
            "nullable": true,
            "description": "Optional raw Gmail thread id (hex) to send onto an existing thread."
          }
        }
      },
      "UpdateWebhookRequest": {
        "type": "object",
        "description": "Provide at least one field. `status` toggles delivery: `disabled` pauses, `active` resumes (and clears the failure streak).",
        "properties": {
          "events": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            },
            "description": "Replace the subscribed event types (at least one valid event)."
          },
          "target_url": {
            "type": "string",
            "nullable": true,
            "description": "Replace the delivery URL."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled"
            ],
            "description": "active = deliver; disabled = pause (events still recorded)."
          },
          "filters": {
            "$ref": "#/components/schemas/WebhookFilters"
          }
        }
      },
      "WebhookFilters": {
        "type": "object",
        "nullable": true,
        "description": "Optional per-subscription delivery filters. `scope` applies to `thread.reply_received`: `tasked` (default) delivers only replies on threads you have an accessible linked task for; `all` delivers every inbound message. Send `null` to clear.",
        "properties": {
          "scope": {
            "type": "string",
            "enum": [
              "tasked",
              "all"
            ],
            "description": "thread.reply_received delivery scope. Default tasked."
          }
        }
      },
      "CreateWebhookRequest": {
        "type": "object",
        "required": [
          "target_url",
          "events"
        ],
        "description": "Subscribe a target url to one or more event types. The response includes the signing secret once.",
        "properties": {
          "target_url": {
            "type": "string",
            "description": "HTTPS endpoint that receives signed event deliveries."
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Event types to subscribe to (at least one valid event)."
          },
          "filters": {
            "$ref": "#/components/schemas/WebhookFilters"
          }
        }
      },
      "ReadStateRequest": {
        "type": "object",
        "required": [
          "read"
        ],
        "properties": {
          "read": {
            "type": "boolean",
            "description": "true = mark read, false = mark unread."
          }
        }
      },
      "LabelsRequest": {
        "type": "object",
        "description": "At least one of add/remove is required.",
        "properties": {
          "add": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            },
            "description": "Raw Gmail label ids to add."
          },
          "remove": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            },
            "description": "Raw Gmail label ids to remove."
          }
        }
      },
      "SnoozeRequest": {
        "type": "object",
        "required": [
          "until"
        ],
        "properties": {
          "until": {
            "type": "string",
            "format": "date-time",
            "description": "When the thread should return to the Inbox. Must be in the future."
          }
        }
      },
      "Mailbox": {
        "type": "object",
        "description": "The key owner's mailbox connection state. A v2 key reaches only its owner's own mailbox (Flow A).",
        "properties": {
          "connected": {
            "type": "boolean",
            "description": "True when a mailbox is connected and its stored credentials are usable."
          },
          "provider_id": {
            "type": "string",
            "nullable": true,
            "description": "Email provider id, e.g. itemprovider-gmail. Null when not connected."
          },
          "email_address": {
            "type": "string",
            "nullable": true,
            "description": "The mailbox address. Null when not connected."
          },
          "reason": {
            "type": "string",
            "nullable": true,
            "enum": [
              null,
              "not_connected",
              "reauth_required"
            ],
            "description": "Why connected is false: not_connected = no email account linked; reauth_required = the stored authorisation is no longer usable and the user must reconnect their mailbox in Sortd."
          }
        }
      },
      "ErrorEnvelope": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              },
              "details": {}
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "operationId": "getMe",
        "summary": "Get the authenticated principal",
        "tags": [
          "Identity"
        ],
        "description": "Returns the user and the API key the request authenticated with. Requires no scopes — the quickest way to confirm a key works.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Me"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/me\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/me\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/me\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/me\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/teams": {
      "get": {
        "operationId": "listTeams",
        "summary": "List teams the API is enabled for",
        "tags": [
          "Teams"
        ],
        "description": "The teams (organisations) your key can act within. A team `id` is the `team_id` you pass to list boards.",
        "x-required-scopes": [
          "boards.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "boards.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Team"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        },
                        "pagination": {
                          "type": "object",
                          "properties": {
                            "next_cursor": {
                              "type": "string",
                              "nullable": true
                            },
                            "has_more": {
                              "type": "boolean"
                            },
                            "limit": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/teams\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/teams\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/teams\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/teams\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/teams/{id}": {
      "get": {
        "operationId": "getTeam",
        "summary": "Get a team",
        "tags": [
          "Teams"
        ],
        "description": "Fetch a single team by id.",
        "x-required-scopes": [
          "boards.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "boards.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Team id",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Team"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/teams/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/teams/<id>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/teams/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/teams/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/boards": {
      "get": {
        "operationId": "listBoards",
        "summary": "List boards for a team",
        "tags": [
          "Boards"
        ],
        "description": "Boards a team owns. Use `team_id` from `/me` or `/teams`; the returned `id` (brd_…) feeds the lists and tasks endpoints.",
        "x-required-scopes": [
          "boards.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "boards.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "team_id",
            "in": "query",
            "required": true,
            "description": "Team id",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Board"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        },
                        "pagination": {
                          "type": "object",
                          "properties": {
                            "next_cursor": {
                              "type": "string",
                              "nullable": true
                            },
                            "has_more": {
                              "type": "boolean"
                            },
                            "limit": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/boards?team_id=<team_id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/boards?team_id=<team_id>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/boards?team_id=<team_id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/boards?team_id=<team_id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      },
      "post": {
        "operationId": "createBoard",
        "summary": "Create a board",
        "tags": [
          "Boards"
        ],
        "description": "Creates a shared team board under a team. `team_id` and `name` are required. Returns the created board with its opaque id (brd_…).",
        "x-required-scopes": [
          "boards.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "boards.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateBoardRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Board"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/boards\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"team_id\": \"<team_id>\",\n  \"name\": \"My key\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/boards\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"team_id\": \"<team_id>\",\n  \"name\": \"My key\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"team_id\": \"<team_id>\",\n  \"name\": \"My key\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/boards\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/boards\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"team_id\": \"<team_id>\",\n        \"name\": \"My key\"\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/boards/{id}": {
      "get": {
        "operationId": "getBoard",
        "summary": "Get a board",
        "tags": [
          "Boards"
        ],
        "description": "Fetch a single board by its opaque id (brd_…).",
        "x-required-scopes": [
          "boards.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "boards.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque board id (brd_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Board"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/boards/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/boards/<id>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/boards/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/boards/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      },
      "patch": {
        "operationId": "updateBoard",
        "summary": "Rename a board",
        "tags": [
          "Boards"
        ],
        "description": "Renames a board. Send `name`. Returns the updated board.",
        "x-required-scopes": [
          "boards.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "boards.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque board id (brd_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateBoardRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Board"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X PATCH \"https://api.sortd.com/v2/boards/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"name\": \"My key\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/boards/<id>\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n  \"name\": \"My key\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"name\": \"My key\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PatchAsync(\"https://api.sortd.com/v2/boards/<id>\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.patch(\n    \"https://api.sortd.com/v2/boards/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"name\": \"My key\"\n    },\n)\ndata = res.json()"
          }
        ]
      },
      "delete": {
        "operationId": "deleteBoard",
        "summary": "Delete a board",
        "tags": [
          "Boards"
        ],
        "description": "Soft-deletes a board (it stops appearing in reads but is recoverable). Returns the board id with `deleted: true`.",
        "x-required-scopes": [
          "boards.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "boards.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque board id (brd_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X DELETE \"https://api.sortd.com/v2/boards/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/boards/<id>\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.DeleteAsync(\"https://api.sortd.com/v2/boards/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    \"https://api.sortd.com/v2/boards/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/boards/{id}/lists": {
      "get": {
        "operationId": "listBoardLists",
        "summary": "List a board's task lists",
        "tags": [
          "Boards"
        ],
        "description": "The lists (columns) on a board. A list `id` (lst_…) is where you create or move tasks.",
        "x-required-scopes": [
          "lists.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "lists.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque board id (brd_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/List"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        },
                        "pagination": {
                          "type": "object",
                          "properties": {
                            "next_cursor": {
                              "type": "string",
                              "nullable": true
                            },
                            "has_more": {
                              "type": "boolean"
                            },
                            "limit": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/boards/<id>/lists\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/boards/<id>/lists\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/boards/<id>/lists\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/boards/<id>/lists\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      },
      "post": {
        "operationId": "createList",
        "summary": "Create a list on a board",
        "tags": [
          "Lists"
        ],
        "description": "Adds a task list (column) to a board. Send `name`. Returns the created list with its opaque id (lst_…).",
        "x-required-scopes": [
          "lists.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "lists.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque board id (brd_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateListRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/List"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/boards/<id>/lists\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"name\": \"My key\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/boards/<id>/lists\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"name\": \"My key\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"name\": \"My key\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/boards/<id>/lists\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/boards/<id>/lists\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"name\": \"My key\"\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/boards/{id}/custom-fields": {
      "get": {
        "operationId": "listBoardCustomFields",
        "summary": "List a board's custom fields",
        "tags": [
          "Boards"
        ],
        "description": "The board’s custom fields with their `type` and, for tag/status/select fields, the `options` (id + label). Use a field `id` as `field_id` when setting values via PATCH /tasks/{id}. Fields flagged `ai: true` are managed by Sortd AI and are read-only.",
        "x-required-scopes": [
          "customfields.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "customfields.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque board id (brd_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CustomField"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        },
                        "pagination": {
                          "type": "object",
                          "properties": {
                            "next_cursor": {
                              "type": "string",
                              "nullable": true
                            },
                            "has_more": {
                              "type": "boolean"
                            },
                            "limit": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/boards/<id>/custom-fields\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/boards/<id>/custom-fields\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/boards/<id>/custom-fields\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/boards/<id>/custom-fields\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      },
      "post": {
        "operationId": "createCustomField",
        "summary": "Create a custom field on a board",
        "tags": [
          "Custom fields"
        ],
        "description": "Creates a custom-field definition on a board. Send `name` and `type`; for option types (select/status/tag/multiselect) include `options`. For status/tag fields you can also set `display_on_card` to show the field on task cards. Returns the created field.",
        "x-required-scopes": [
          "customfields.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "customfields.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque board id (brd_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCustomFieldRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CustomField"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/boards/<id>/custom-fields\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"name\": \"My key\",\n  \"type\": \"text\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/boards/<id>/custom-fields\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"name\": \"My key\",\n  \"type\": \"text\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"name\": \"My key\",\n  \"type\": \"text\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/boards/<id>/custom-fields\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/boards/<id>/custom-fields\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"name\": \"My key\",\n        \"type\": \"text\"\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/lists/{id}": {
      "patch": {
        "operationId": "updateList",
        "summary": "Rename a list",
        "tags": [
          "Lists"
        ],
        "description": "Renames a list. Send `name`. Returns the updated list.",
        "x-required-scopes": [
          "lists.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "lists.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque list id (lst_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateListRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/List"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X PATCH \"https://api.sortd.com/v2/lists/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"name\": \"My key\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/lists/<id>\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n  \"name\": \"My key\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"name\": \"My key\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PatchAsync(\"https://api.sortd.com/v2/lists/<id>\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.patch(\n    \"https://api.sortd.com/v2/lists/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"name\": \"My key\"\n    },\n)\ndata = res.json()"
          }
        ]
      },
      "delete": {
        "operationId": "deleteList",
        "summary": "Delete a list",
        "tags": [
          "Lists"
        ],
        "description": "Soft-deletes a list (it stops appearing in reads but the list and its tasks are recoverable). Returns the list id with `deleted: true`.",
        "x-required-scopes": [
          "lists.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "lists.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque list id (lst_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X DELETE \"https://api.sortd.com/v2/lists/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/lists/<id>\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.DeleteAsync(\"https://api.sortd.com/v2/lists/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    \"https://api.sortd.com/v2/lists/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/boards/{id}/custom-fields/{fieldId}": {
      "patch": {
        "operationId": "updateCustomField",
        "summary": "Update a custom field",
        "tags": [
          "Custom fields"
        ],
        "description": "Partial update of a custom-field definition — send `name`, `options`, and/or `display_on_card` (status/tag fields only). Returns the updated field.",
        "x-required-scopes": [
          "customfields.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "customfields.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque board id (brd_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fieldId",
            "in": "path",
            "required": true,
            "description": "Custom-field id",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCustomFieldRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CustomField"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X PATCH \"https://api.sortd.com/v2/boards/<id>/custom-fields/<fieldId>\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"name\": \"My key\",\n  \"options\": [],\n  \"display_on_card\": true\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/boards/<id>/custom-fields/<fieldId>\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n  \"name\": \"My key\",\n  \"options\": [],\n  \"display_on_card\": true\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"name\": \"My key\",\n  \"options\": [],\n  \"display_on_card\": true\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PatchAsync(\"https://api.sortd.com/v2/boards/<id>/custom-fields/<fieldId>\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.patch(\n    \"https://api.sortd.com/v2/boards/<id>/custom-fields/<fieldId>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"name\": \"My key\",\n        \"options\": [],\n        \"display_on_card\": True\n    },\n)\ndata = res.json()"
          }
        ]
      },
      "delete": {
        "operationId": "deleteCustomField",
        "summary": "Delete a custom field",
        "tags": [
          "Custom fields"
        ],
        "description": "Soft-deletes a custom field from a board (it stops appearing in reads but its values are preserved and recoverable). Returns the field id with `deleted: true`.",
        "x-required-scopes": [
          "customfields.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "customfields.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque board id (brd_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fieldId",
            "in": "path",
            "required": true,
            "description": "Custom-field id",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X DELETE \"https://api.sortd.com/v2/boards/<id>/custom-fields/<fieldId>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/boards/<id>/custom-fields/<fieldId>\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.DeleteAsync(\"https://api.sortd.com/v2/boards/<id>/custom-fields/<fieldId>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    \"https://api.sortd.com/v2/boards/<id>/custom-fields/<fieldId>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/tasks": {
      "get": {
        "operationId": "listTasks",
        "summary": "List or search tasks on a board, list, email thread, or CRM contact",
        "tags": [
          "Tasks"
        ],
        "description": "A scope is required (mutually exclusive, precedence `thread_id` → `contact_id` → `list_id` → `board_id`): pass `thread_id` (hex Gmail thread id) for tasks linked to a thread, `contact_id` (con_…) for tasks linked to a CRM contact (sale cards + contact custom-field links), `list_id` for a list, or `board_id` for a board. (There is no cross-board \"all my tasks\" listing.) Optional search/filter (applies to the board / list scopes, not thread/contact): `q` (case-insensitive match on title + notes), `completed`, `shade`, `due_before`/`due_after`, `assigned_to`, and `custom_field_id` (+ optional `custom_field_value`). Only tasks on boards you are a member of are ever returned. Cursor-paginated — pass `meta.pagination.next_cursor` back as `cursor`; `limit` caps page size (max 200).",
        "x-required-scopes": [
          "tasks.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "tasks.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "board_id",
            "in": "query",
            "required": false,
            "description": "Opaque board id (brd_…) — tasks on this board",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "list_id",
            "in": "query",
            "required": false,
            "description": "Opaque list id (lst_…) — tasks on this list",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "thread_id",
            "in": "query",
            "required": false,
            "description": "Email thread id (hex) — tasks linked to that thread",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "contact_id",
            "in": "query",
            "required": false,
            "description": "Opaque contact id (con_…) — tasks linked to that CRM contact",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search text — case-insensitive substring match on title and notes",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "completed",
            "in": "query",
            "required": false,
            "description": "Filter by completion (true/false)",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "shade",
            "in": "query",
            "required": false,
            "description": "Filter by task colour name (e.g. \"red\", \"none\")",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "due_before",
            "in": "query",
            "required": false,
            "description": "Only tasks due on/before this ISO-8601 datetime",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "due_after",
            "in": "query",
            "required": false,
            "description": "Only tasks due on/after this ISO-8601 datetime",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "assigned_to",
            "in": "query",
            "required": false,
            "description": "Filter by assignee email (exact) or display name (substring)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "custom_field_id",
            "in": "query",
            "required": false,
            "description": "Filter to tasks that have this custom field set",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "custom_field_value",
            "in": "query",
            "required": false,
            "description": "With custom_field_id — require this value (matches a scalar value or membership of a multi-value field)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (max 200)",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque pagination cursor",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Task"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        },
                        "pagination": {
                          "type": "object",
                          "properties": {
                            "next_cursor": {
                              "type": "string",
                              "nullable": true
                            },
                            "has_more": {
                              "type": "boolean"
                            },
                            "limit": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/tasks?board_id=<board_id>&list_id=<list_id>&thread_id=<thread_id>&contact_id=<contact_id>&q=<q>&completed=<completed>&shade=<shade>&due_before=<due_before>&due_after=<due_after>&assigned_to=<assigned_to>&custom_field_id=<custom_field_id>&custom_field_value=<custom_field_value>&limit=<limit>&cursor=<cursor>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/tasks?board_id=<board_id>&list_id=<list_id>&thread_id=<thread_id>&contact_id=<contact_id>&q=<q>&completed=<completed>&shade=<shade>&due_before=<due_before>&due_after=<due_after>&assigned_to=<assigned_to>&custom_field_id=<custom_field_id>&custom_field_value=<custom_field_value>&limit=<limit>&cursor=<cursor>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/tasks?board_id=<board_id>&list_id=<list_id>&thread_id=<thread_id>&contact_id=<contact_id>&q=<q>&completed=<completed>&shade=<shade>&due_before=<due_before>&due_after=<due_after>&assigned_to=<assigned_to>&custom_field_id=<custom_field_id>&custom_field_value=<custom_field_value>&limit=<limit>&cursor=<cursor>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/tasks?board_id=<board_id>&list_id=<list_id>&thread_id=<thread_id>&contact_id=<contact_id>&q=<q>&completed=<completed>&shade=<shade>&due_before=<due_before>&due_after=<due_after>&assigned_to=<assigned_to>&custom_field_id=<custom_field_id>&custom_field_value=<custom_field_value>&limit=<limit>&cursor=<cursor>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      },
      "post": {
        "operationId": "createTask",
        "summary": "Create a task",
        "tags": [
          "Tasks"
        ],
        "description": "Creates a task in a list. `list_id` and `title` are required; `notes` and `due_at` optional. Send an `Idempotency-Key` header so a retried request returns the same task instead of creating a duplicate.",
        "x-required-scopes": [
          "tasks.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "tasks.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTaskRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Task"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/tasks\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"list_id\": \"<list_id>\",\n  \"title\": \"Follow up with Acme\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/tasks\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"list_id\": \"<list_id>\",\n  \"title\": \"Follow up with Acme\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"list_id\": \"<list_id>\",\n  \"title\": \"Follow up with Acme\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/tasks\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/tasks\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"list_id\": \"<list_id>\",\n        \"title\": \"Follow up with Acme\"\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/tasks/{id}": {
      "get": {
        "operationId": "getTask",
        "summary": "Get a task",
        "tags": [
          "Tasks"
        ],
        "description": "Fetch a single task by its opaque id (tsk_…), including its colours and custom-field values.",
        "x-required-scopes": [
          "tasks.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "tasks.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque task id (tsk_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Task"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/tasks/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/tasks/<id>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/tasks/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/tasks/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      },
      "patch": {
        "operationId": "updateTask",
        "summary": "Update a task",
        "tags": [
          "Tasks"
        ],
        "description": "Partial update — send only the fields to change. Supports `shade` (the task colour; a named enum, `\"none\"` clears) and `custom_fields` (set values by `field_id`; for tag/status/select pass option id(s) from GET /boards/{id}/custom-fields). Setting custom_fields additionally requires the `customfields.write` scope. Returns the updated task.",
        "x-required-scopes": [
          "tasks.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "tasks.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque task id (tsk_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateTaskRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Task"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X PATCH \"https://api.sortd.com/v2/tasks/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"shade\": \"none\",\n  \"custom_fields\": []\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/tasks/<id>\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n  \"shade\": \"none\",\n  \"custom_fields\": []\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"shade\": \"none\",\n  \"custom_fields\": []\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PatchAsync(\"https://api.sortd.com/v2/tasks/<id>\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.patch(\n    \"https://api.sortd.com/v2/tasks/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"shade\": \"none\",\n        \"custom_fields\": []\n    },\n)\ndata = res.json()"
          }
        ]
      },
      "delete": {
        "operationId": "deleteTask",
        "summary": "Delete a task",
        "tags": [
          "Tasks"
        ],
        "description": "Soft-deletes a task (it stops appearing in reads but is recoverable). Any synced email thread is left intact. Returns the task id with `deleted: true`.",
        "x-required-scopes": [
          "tasks.delete"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "tasks.delete"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque task id (tsk_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X DELETE \"https://api.sortd.com/v2/tasks/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/tasks/<id>\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.DeleteAsync(\"https://api.sortd.com/v2/tasks/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    \"https://api.sortd.com/v2/tasks/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/tasks/{id}/complete": {
      "post": {
        "operationId": "completeTask",
        "summary": "Mark a task complete",
        "tags": [
          "Tasks"
        ],
        "description": "Marks the task complete. Idempotent — completing an already-complete task is a no-op. Returns the updated task.",
        "x-required-scopes": [
          "tasks.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "tasks.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque task id (tsk_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Task"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/tasks/<id>/complete\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/tasks/<id>/complete\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/tasks/<id>/complete\", null);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/tasks/<id>/complete\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/tasks/{id}/reopen": {
      "post": {
        "operationId": "reopenTask",
        "summary": "Reopen a completed task",
        "tags": [
          "Tasks"
        ],
        "description": "Reopens a completed task. Returns the updated task.",
        "x-required-scopes": [
          "tasks.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "tasks.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque task id (tsk_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Task"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/tasks/<id>/reopen\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/tasks/<id>/reopen\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/tasks/<id>/reopen\", null);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/tasks/<id>/reopen\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/tasks/{id}/move": {
      "post": {
        "operationId": "moveTask",
        "summary": "Move a task to another list",
        "tags": [
          "Tasks"
        ],
        "description": "Moves the task to the destination `list_id` (on any board the key can access). Returns the updated task.",
        "x-required-scopes": [
          "tasks.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "tasks.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque task id (tsk_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoveTaskRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Task"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/tasks/<id>/move\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"list_id\": \"<list_id>\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/tasks/<id>/move\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"list_id\": \"<list_id>\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"list_id\": \"<list_id>\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/tasks/<id>/move\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/tasks/<id>/move\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"list_id\": \"<list_id>\"\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/contacts": {
      "get": {
        "operationId": "searchContacts",
        "summary": "Search a team's CRM contacts",
        "tags": [
          "Contacts"
        ],
        "description": "Searches the contacts of one team you belong to (pass the required `team_id`). Results are **company-grouped** — each entry is a company with a matching contact. `q` does a case-insensitive match on contact name, company name and contact-info values; `type` and `stage` filter together (e.g. type=sale & stage=Lead). Returns only contacts in that team (per-contact visibility is not applied — it mirrors the in-product view where members see the team's contacts). Requires the `contacts.read` scope and the team's Contacts subscription feature. Cursor-paginated; `limit` max 200.",
        "x-required-scopes": [
          "contacts.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "contacts.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "team_id",
            "in": "query",
            "required": false,
            "description": "The team/organisation id whose contacts to search (required).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search text — matches contact name, company name and contact-info values.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Contact type (e.g. sale, general, hire). Applied together with stage.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "stage",
            "in": "query",
            "required": false,
            "description": "Contact stage (e.g. Lead, Customer). Applied together with type.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (max 200)",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque pagination cursor",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Contact"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        },
                        "pagination": {
                          "type": "object",
                          "properties": {
                            "next_cursor": {
                              "type": "string",
                              "nullable": true
                            },
                            "has_more": {
                              "type": "boolean"
                            },
                            "limit": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/contacts?team_id=<team_id>&q=<q>&type=<type>&stage=<stage>&limit=<limit>&cursor=<cursor>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/contacts?team_id=<team_id>&q=<q>&type=<type>&stage=<stage>&limit=<limit>&cursor=<cursor>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/contacts?team_id=<team_id>&q=<q>&type=<type>&stage=<stage>&limit=<limit>&cursor=<cursor>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/contacts?team_id=<team_id>&q=<q>&type=<type>&stage=<stage>&limit=<limit>&cursor=<cursor>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      },
      "post": {
        "operationId": "createContact",
        "summary": "Create a CRM contact",
        "tags": [
          "Contacts"
        ],
        "description": "Create a contact in a team (`team_id` required). Link a company via `company_id` (existing) or `company_name` (find-or-create); with neither, the org default company is used. Requires the Contacts subscription feature.",
        "x-required-scopes": [
          "contacts.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "contacts.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateContactRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/contacts\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"team_id\": \"<team_id>\",\n  \"name\": \"My key\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/contacts\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"team_id\": \"<team_id>\",\n  \"name\": \"My key\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"team_id\": \"<team_id>\",\n  \"name\": \"My key\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/contacts\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/contacts\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"team_id\": \"<team_id>\",\n        \"name\": \"My key\"\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/contacts/{id}": {
      "get": {
        "operationId": "getContact",
        "summary": "Get a CRM contact",
        "tags": [
          "Contacts"
        ],
        "description": "Fetch a single contact by its opaque id (con_…), including its company.",
        "x-required-scopes": [
          "contacts.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "contacts.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque contact id (con_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/contacts/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/contacts/<id>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/contacts/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/contacts/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      },
      "patch": {
        "operationId": "updateContact",
        "summary": "Update a CRM contact",
        "tags": [
          "Contacts"
        ],
        "description": "Partial update — provide only the fields to change. `company_id` re-links to a different company.",
        "x-required-scopes": [
          "contacts.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "contacts.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque contact id (con_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateContactRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X PATCH \"https://api.sortd.com/v2/contacts/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"name\": \"My key\",\n  \"first_name\": \"<first_name>\",\n  \"last_name\": \"<last_name>\",\n  \"title\": \"Follow up with Acme\",\n  \"type\": \"<type>\",\n  \"stage\": \"<stage>\",\n  \"source\": \"<source>\",\n  \"contact_info\": [],\n  \"tags\": [],\n  \"company_id\": \"<company_id>\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/contacts/<id>\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n  \"name\": \"My key\",\n  \"first_name\": \"<first_name>\",\n  \"last_name\": \"<last_name>\",\n  \"title\": \"Follow up with Acme\",\n  \"type\": \"<type>\",\n  \"stage\": \"<stage>\",\n  \"source\": \"<source>\",\n  \"contact_info\": [],\n  \"tags\": [],\n  \"company_id\": \"<company_id>\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"name\": \"My key\",\n  \"first_name\": \"<first_name>\",\n  \"last_name\": \"<last_name>\",\n  \"title\": \"Follow up with Acme\",\n  \"type\": \"<type>\",\n  \"stage\": \"<stage>\",\n  \"source\": \"<source>\",\n  \"contact_info\": [],\n  \"tags\": [],\n  \"company_id\": \"<company_id>\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PatchAsync(\"https://api.sortd.com/v2/contacts/<id>\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.patch(\n    \"https://api.sortd.com/v2/contacts/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"name\": \"My key\",\n        \"first_name\": \"<first_name>\",\n        \"last_name\": \"<last_name>\",\n        \"title\": \"Follow up with Acme\",\n        \"type\": \"<type>\",\n        \"stage\": \"<stage>\",\n        \"source\": \"<source>\",\n        \"contact_info\": [],\n        \"tags\": [],\n        \"company_id\": \"<company_id>\"\n    },\n)\ndata = res.json()"
          }
        ]
      },
      "delete": {
        "operationId": "deleteContact",
        "summary": "Delete a CRM contact",
        "tags": [
          "Contacts"
        ],
        "description": "Soft-deletes the contact (recoverable; stops appearing in reads).",
        "x-required-scopes": [
          "contacts.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "contacts.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque contact id (con_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X DELETE \"https://api.sortd.com/v2/contacts/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/contacts/<id>\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.DeleteAsync(\"https://api.sortd.com/v2/contacts/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    \"https://api.sortd.com/v2/contacts/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/companies": {
      "get": {
        "operationId": "listCompanies",
        "summary": "List a team's CRM companies",
        "tags": [
          "Companies"
        ],
        "description": "Companies in the team (`team_id` required); optional `q` name filter. Cursor-paginated. Requires the Contacts subscription feature.",
        "x-required-scopes": [
          "contacts.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "contacts.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "team_id",
            "in": "query",
            "required": false,
            "description": "Team/organisation id (required).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Filter by company name (substring).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (max 200)",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque pagination cursor",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Company"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        },
                        "pagination": {
                          "type": "object",
                          "properties": {
                            "next_cursor": {
                              "type": "string",
                              "nullable": true
                            },
                            "has_more": {
                              "type": "boolean"
                            },
                            "limit": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/companies?team_id=<team_id>&q=<q>&limit=<limit>&cursor=<cursor>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/companies?team_id=<team_id>&q=<q>&limit=<limit>&cursor=<cursor>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/companies?team_id=<team_id>&q=<q>&limit=<limit>&cursor=<cursor>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/companies?team_id=<team_id>&q=<q>&limit=<limit>&cursor=<cursor>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      },
      "post": {
        "operationId": "createCompany",
        "summary": "Create a CRM company",
        "tags": [
          "Companies"
        ],
        "description": "Create a company in a team (`team_id` required). Requires the Contacts subscription feature.",
        "x-required-scopes": [
          "contacts.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "contacts.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCompanyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Company"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/companies\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"team_id\": \"<team_id>\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/companies\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"team_id\": \"<team_id>\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"team_id\": \"<team_id>\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/companies\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/companies\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"team_id\": \"<team_id>\"\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/companies/{id}": {
      "get": {
        "operationId": "getCompany",
        "summary": "Get a CRM company",
        "tags": [
          "Companies"
        ],
        "description": "Fetch a single company by its opaque id (cmp_…).",
        "x-required-scopes": [
          "contacts.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "contacts.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque company id (cmp_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Company"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/companies/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/companies/<id>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/companies/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/companies/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      },
      "patch": {
        "operationId": "updateCompany",
        "summary": "Update a CRM company",
        "tags": [
          "Companies"
        ],
        "description": "Partial update — provide only the fields to change.",
        "x-required-scopes": [
          "contacts.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "contacts.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque company id (cmp_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCompanyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Company"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X PATCH \"https://api.sortd.com/v2/companies/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"name\": \"My key\",\n  \"type\": \"<type>\",\n  \"stage\": \"<stage>\",\n  \"company_info\": [],\n  \"tags\": []\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/companies/<id>\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n  \"name\": \"My key\",\n  \"type\": \"<type>\",\n  \"stage\": \"<stage>\",\n  \"company_info\": [],\n  \"tags\": []\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"name\": \"My key\",\n  \"type\": \"<type>\",\n  \"stage\": \"<stage>\",\n  \"company_info\": [],\n  \"tags\": []\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PatchAsync(\"https://api.sortd.com/v2/companies/<id>\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.patch(\n    \"https://api.sortd.com/v2/companies/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"name\": \"My key\",\n        \"type\": \"<type>\",\n        \"stage\": \"<stage>\",\n        \"company_info\": [],\n        \"tags\": []\n    },\n)\ndata = res.json()"
          }
        ]
      },
      "delete": {
        "operationId": "deleteCompany",
        "summary": "Delete a CRM company",
        "tags": [
          "Companies"
        ],
        "description": "Soft-deletes the company AND its contacts (cascade), mirroring the in-product behaviour.",
        "x-required-scopes": [
          "contacts.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "contacts.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque company id (cmp_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X DELETE \"https://api.sortd.com/v2/companies/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/companies/<id>\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.DeleteAsync(\"https://api.sortd.com/v2/companies/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    \"https://api.sortd.com/v2/companies/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/webhooks/subscriptions": {
      "post": {
        "operationId": "createWebhook",
        "summary": "Create a webhook subscription",
        "tags": [
          "Webhooks"
        ],
        "description": "Subscribe a `target_url` to one or more event types. Optionally set `filters.scope` (`tasked`|`all`) to control `thread.reply_received` delivery. The response includes a signing `secret` (whsec_…), shown once — store it to verify the `x-sortd-signature` on each delivery.",
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "webhooks.manage"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/webhooks/subscriptions\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"target_url\": \"<target_url>\",\n  \"events\": []\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/webhooks/subscriptions\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"target_url\": \"<target_url>\",\n  \"events\": []\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"target_url\": \"<target_url>\",\n  \"events\": []\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/webhooks/subscriptions\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/webhooks/subscriptions\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"target_url\": \"<target_url>\",\n        \"events\": []\n    },\n)\ndata = res.json()"
          }
        ]
      },
      "get": {
        "operationId": "listWebhooks",
        "summary": "List webhook subscriptions",
        "tags": [
          "Webhooks"
        ],
        "description": "Your webhook subscriptions (target url, events, status) — never the signing secret.",
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "webhooks.manage"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/webhooks/subscriptions\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/webhooks/subscriptions\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/webhooks/subscriptions\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/webhooks/subscriptions\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/webhooks/subscriptions/{id}": {
      "delete": {
        "operationId": "deleteWebhook",
        "summary": "Delete a webhook subscription",
        "tags": [
          "Webhooks"
        ],
        "description": "Removes a subscription. Deliveries stop immediately.",
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "webhooks.manage"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque webhook id (wh_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X DELETE \"https://api.sortd.com/v2/webhooks/subscriptions/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/webhooks/subscriptions/<id>\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.DeleteAsync(\"https://api.sortd.com/v2/webhooks/subscriptions/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    \"https://api.sortd.com/v2/webhooks/subscriptions/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      },
      "patch": {
        "operationId": "updateWebhook",
        "summary": "Update or pause a webhook subscription",
        "tags": [
          "Webhooks"
        ],
        "description": "Change a subscription’s `events`, `target_url`, or `status`. Set `status` to `disabled` to pause delivery (events are still recorded) or `active` to resume — resuming clears the failure streak. Note: a subscription is auto-disabled after repeated failed deliveries; resume it here once the endpoint is healthy.",
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "webhooks.manage"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque webhook id (wh_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X PATCH \"https://api.sortd.com/v2/webhooks/subscriptions/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n  \"events\": [],\n  \"target_url\": \"<target_url>\",\n  \"status\": \"active\",\n  \"filters\": \"<filters>\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/webhooks/subscriptions/<id>\", {\n  method: \"PATCH\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n  \"events\": [],\n  \"target_url\": \"<target_url>\",\n  \"status\": \"active\",\n  \"filters\": \"<filters>\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"events\": [],\n  \"target_url\": \"<target_url>\",\n  \"status\": \"active\",\n  \"filters\": \"<filters>\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PatchAsync(\"https://api.sortd.com/v2/webhooks/subscriptions/<id>\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.patch(\n    \"https://api.sortd.com/v2/webhooks/subscriptions/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"events\": [],\n        \"target_url\": \"<target_url>\",\n        \"status\": \"active\",\n        \"filters\": \"<filters>\"\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/webhooks/subscriptions/{id}/rotate-secret": {
      "post": {
        "operationId": "rotateWebhookSecret",
        "summary": "Rotate a webhook signing secret",
        "tags": [
          "Webhooks"
        ],
        "description": "Issues a new signing secret (whsec_…), shown once. Update your signature verifier when you rotate.",
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "webhooks.manage"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque webhook id (wh_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/webhooks/subscriptions/<id>/rotate-secret\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/webhooks/subscriptions/<id>/rotate-secret\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/webhooks/subscriptions/<id>/rotate-secret\", null);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/webhooks/subscriptions/<id>/rotate-secret\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/webhooks/subscriptions/{id}/test": {
      "post": {
        "operationId": "testWebhook",
        "summary": "Send a test ping to a webhook",
        "tags": [
          "Webhooks"
        ],
        "description": "Delivers a synthetic `ping` event so you can confirm your endpoint and signature verification work.",
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "webhooks.manage"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque webhook id (wh_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/webhooks/subscriptions/<id>/test\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/webhooks/subscriptions/<id>/test\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/webhooks/subscriptions/<id>/test\", null);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/webhooks/subscriptions/<id>/test\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/webhooks/subscriptions/{id}/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "summary": "List a webhook’s delivery log",
        "tags": [
          "Webhooks"
        ],
        "description": "Recent delivery attempts (status, timing, response code) for debugging. Each has a delivery id you can replay.",
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "webhooks.manage"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opaque webhook id (wh_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/webhooks/subscriptions/<id>/deliveries\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/webhooks/subscriptions/<id>/deliveries\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/webhooks/subscriptions/<id>/deliveries\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/webhooks/subscriptions/<id>/deliveries\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/webhooks/deliveries/{deliveryId}/replay": {
      "post": {
        "operationId": "replayWebhookDelivery",
        "summary": "Replay a webhook delivery",
        "tags": [
          "Webhooks"
        ],
        "description": "Re-sends a past delivery by its id — handy after fixing your endpoint.",
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "webhooks.manage"
            ]
          }
        ],
        "parameters": [
          {
            "name": "deliveryId",
            "in": "path",
            "required": true,
            "description": "Opaque delivery id",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/webhooks/deliveries/<deliveryId>/replay\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/webhooks/deliveries/<deliveryId>/replay\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/webhooks/deliveries/<deliveryId>/replay\", null);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/webhooks/deliveries/<deliveryId>/replay\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/zapier/hooks": {
      "post": {
        "operationId": "zapierSubscribe",
        "summary": "Zapier REST Hook subscribe",
        "tags": [
          "Zapier"
        ],
        "description": "Registers a target URL for an event type (used by the Zapier app’s REST Hook triggers).",
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "webhooks.manage"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/zapier/hooks\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/zapier/hooks\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/zapier/hooks\", null);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/zapier/hooks\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/zapier/hooks/{id}": {
      "delete": {
        "operationId": "zapierUnsubscribe",
        "summary": "Zapier REST Hook unsubscribe",
        "tags": [
          "Zapier"
        ],
        "description": "Removes a Zapier REST Hook subscription by id (called when a Zap is turned off).",
        "x-required-scopes": [
          "webhooks.manage"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "webhooks.manage"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Subscription id (wh_…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X DELETE \"https://api.sortd.com/v2/zapier/hooks/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/zapier/hooks/<id>\", {\n  method: \"DELETE\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.DeleteAsync(\"https://api.sortd.com/v2/zapier/hooks/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.delete(\n    \"https://api.sortd.com/v2/zapier/hooks/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/zapier/poll": {
      "get": {
        "operationId": "zapierPoll",
        "summary": "Zapier polling trigger (event feed since cursor)",
        "tags": [
          "Zapier"
        ],
        "description": "Returns events of the given type newer than the `since` cursor — the polling fallback for Zapier triggers.",
        "x-required-scopes": [
          "tasks.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "tasks.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "event",
            "in": "query",
            "required": true,
            "description": "Event type to poll",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "ISO timestamp cursor",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/zapier/poll?event=<event>&since=<since>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/zapier/poll?event=<event>&since=<since>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/zapier/poll?event=<event>&since=<since>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/zapier/poll?event=<event>&since=<since>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/zapier/sample": {
      "get": {
        "operationId": "zapierSample",
        "summary": "Sample event payload for Zapier setup",
        "tags": [
          "Zapier"
        ],
        "description": "A representative payload for an event type — Zapier uses it to map fields when building a Zap.",
        "x-required-scopes": [
          "tasks.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "tasks.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "event",
            "in": "query",
            "required": true,
            "description": "Event type",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/zapier/sample?event=<event>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/zapier/sample?event=<event>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/zapier/sample?event=<event>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/zapier/sample?event=<event>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email/mailbox": {
      "get": {
        "operationId": "getMailbox",
        "summary": "Get the key owner's mailbox connection state",
        "tags": [
          "Email"
        ],
        "description": "Probes the mailbox the API key can reach (always its owner's own — Flow A). Verifies the stored email credentials without reading any mail and reports `connected` with a `reason` when false. The quickest way to confirm email endpoints will work for this key.",
        "x-required-scopes": [
          "email.metadata.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "email.metadata.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Mailbox"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/email/mailbox\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email/mailbox\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/email/mailbox\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/email/mailbox\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email/labels": {
      "get": {
        "operationId": "listEmailLabels",
        "summary": "List the mailbox's labels/folders",
        "tags": [
          "Email"
        ],
        "description": "The Gmail labels in the key owner's mailbox, each typed `system` (INBOX, SENT, SPAM, TRASH, DRAFT, STARRED, IMPORTANT, CATEGORY_*) or `user` (custom). Use these with the thread-search `label` filter or the `label:`/`in:` operators in `q`. Gmail mailboxes only.",
        "x-required-scopes": [
          "email.metadata.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "email.metadata.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/EmailLabels"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/email/labels\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email/labels\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/email/labels\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/email/labels\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email-threads": {
      "get": {
        "operationId": "searchEmailThreads",
        "summary": "Search the key owner's mailbox (Gmail query)",
        "tags": [
          "Email"
        ],
        "description": "Searches threads in the mailbox the key can reach (Flow A, **Gmail only**). Pass `q` with the full Gmail search syntax — free text plus operators like `in:inbox`, `from:x@y.com`, `to:`, `subject:`, `has:attachment`, `after:`/`before:`, `label:`, `category:`, `is:unread`. `label` and `category` are optional conveniences folded into the query. At least one of `q`, `label` or `category` is required. With no `in:`/folder the search covers All Mail except Spam/Trash; **Spam/Trash are only included when `include_spam_trash=true` or the query targets them** (`in:spam`/`in:trash`/`in:anywhere`); use `in:anywhere` rather than `in:all`. Returns thread metadata only (no bodies, no linked tasks — fetch a thread for those). Pages via an opaque `cursor` (Gmail page token); `limit` caps page size (max 100). Note: matching a folder/label returns threads that have *any* message with that label.",
        "x-required-scopes": [
          "email.metadata.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "email.metadata.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Gmail search query (free text + operators). At least one of q/label/category is required.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "label",
            "in": "query",
            "required": false,
            "description": "Convenience: restrict to a label/folder (folded into the query as label:…)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Convenience: Gmail category (primary|social|promotions|updates|forums)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_spam_trash",
            "in": "query",
            "required": false,
            "description": "Include Spam/Trash (default false; auto-enabled when the query targets them)",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (max 100)",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque page cursor (Gmail page token) from a previous response",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EmailThread"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        },
                        "pagination": {
                          "type": "object",
                          "properties": {
                            "next_cursor": {
                              "type": "string",
                              "nullable": true
                            },
                            "has_more": {
                              "type": "boolean"
                            },
                            "limit": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/email-threads?q=<q>&label=<label>&category=<category>&include_spam_trash=<include_spam_trash>&limit=<limit>&cursor=<cursor>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email-threads?q=<q>&label=<label>&category=<category>&include_spam_trash=<include_spam_trash>&limit=<limit>&cursor=<cursor>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/email-threads?q=<q>&label=<label>&category=<category>&include_spam_trash=<include_spam_trash>&limit=<limit>&cursor=<cursor>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/email-threads?q=<q>&label=<label>&category=<category>&include_spam_trash=<include_spam_trash>&limit=<limit>&cursor=<cursor>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email-threads/{threadId}": {
      "get": {
        "operationId": "getEmailThread",
        "summary": "Get an email thread from the key owner’s mailbox",
        "tags": [
          "Email"
        ],
        "description": "Fetches a thread by its raw Gmail thread id (hex, as used by the Gmail API). Returns metadata — subject, participants, snippet, unread state and any Sortd tasks the thread is linked to. Pass `include=messages` to also get the messages with bodies (requires the email.body.read scope).",
        "x-required-scopes": [
          "email.metadata.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "email.metadata.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "threadId",
            "in": "path",
            "required": true,
            "description": "Raw Gmail thread id (hex)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include",
            "in": "query",
            "required": false,
            "description": "Set to `messages` to include full messages with bodies (email.body.read scope)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/EmailThread"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/email-threads/<threadId>?include=<include>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email-threads/<threadId>?include=<include>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/email-threads/<threadId>?include=<include>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/email-threads/<threadId>?include=<include>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email-threads/{threadId}/read-state": {
      "post": {
        "operationId": "setEmailThreadReadState",
        "summary": "Mark an email thread read or unread",
        "tags": [
          "Email"
        ],
        "description": "Marks the thread read/unread in the owner’s mailbox AND keeps linked Sortd tasks in sync using the product’s rules: task read state derives from its threads; on an assigned task only an assignee’s action can make it read, while anyone marking unread flags it for everyone. Returns the affected tasks with their resulting read state.",
        "x-required-scopes": [
          "email.metadata.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "email.metadata.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "threadId",
            "in": "path",
            "required": true,
            "description": "Raw Gmail thread id (hex)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReadStateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/email-threads/<threadId>/read-state\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"read\": true\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email-threads/<threadId>/read-state\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"read\": true\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"read\": true\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/email-threads/<threadId>/read-state\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/email-threads/<threadId>/read-state\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"read\": True\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email-threads/{threadId}/archive": {
      "post": {
        "operationId": "archiveEmailThread",
        "summary": "Archive an email thread",
        "tags": [
          "Email"
        ],
        "description": "Removes the thread from the mailbox Inbox (Gmail archive). The thread and any linked tasks are unaffected otherwise.",
        "x-required-scopes": [
          "email.metadata.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "email.metadata.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "threadId",
            "in": "path",
            "required": true,
            "description": "Raw Gmail thread id (hex)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/email-threads/<threadId>/archive\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email-threads/<threadId>/archive\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/email-threads/<threadId>/archive\", null);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/email-threads/<threadId>/archive\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email-threads/{threadId}/unarchive": {
      "post": {
        "operationId": "unarchiveEmailThread",
        "summary": "Unarchive an email thread",
        "tags": [
          "Email"
        ],
        "description": "Moves the thread back into the mailbox Inbox.",
        "x-required-scopes": [
          "email.metadata.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "email.metadata.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "threadId",
            "in": "path",
            "required": true,
            "description": "Raw Gmail thread id (hex)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/email-threads/<threadId>/unarchive\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email-threads/<threadId>/unarchive\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/email-threads/<threadId>/unarchive\", null);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/email-threads/<threadId>/unarchive\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email-threads/{threadId}/labels": {
      "post": {
        "operationId": "applyEmailThreadLabels",
        "summary": "Add or remove Gmail labels on a thread",
        "tags": [
          "Email"
        ],
        "description": "Applies raw Gmail label ids to the thread — send `add` and/or `remove` arrays.",
        "x-required-scopes": [
          "email.metadata.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "email.metadata.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "threadId",
            "in": "path",
            "required": true,
            "description": "Raw Gmail thread id (hex)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LabelsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/email-threads/<threadId>/labels\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"add\": [],\n  \"remove\": []\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email-threads/<threadId>/labels\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"add\": [],\n  \"remove\": []\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"add\": [],\n  \"remove\": []\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/email-threads/<threadId>/labels\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/email-threads/<threadId>/labels\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"add\": [],\n        \"remove\": []\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email-threads/{threadId}/snooze": {
      "post": {
        "operationId": "snoozeEmailThread",
        "summary": "Snooze an email thread",
        "tags": [
          "Email"
        ],
        "description": "Archives the thread now and returns it to the Inbox at `until` (the product’s snooze).",
        "x-required-scopes": [
          "email.metadata.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "email.metadata.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "threadId",
            "in": "path",
            "required": true,
            "description": "Raw Gmail thread id (hex)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SnoozeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/email-threads/<threadId>/snooze\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"until\": \"<until>\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email-threads/<threadId>/snooze\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"until\": \"<until>\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"until\": \"<until>\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/email-threads/<threadId>/snooze\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/email-threads/<threadId>/snooze\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"until\": \"<until>\"\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email-threads/{threadId}/drafts": {
      "post": {
        "operationId": "createEmailDraft",
        "summary": "Create a draft on a thread",
        "tags": [
          "Email"
        ],
        "description": "Creates a Gmail draft threaded onto the conversation. The draft lives in Gmail — the user can edit, send or delete it there (or an API key with drafts.send can send it). This scope can never send mail. Send an `Idempotency-Key` header so retries don’t create duplicate drafts.",
        "x-required-scopes": [
          "drafts.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "drafts.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "threadId",
            "in": "path",
            "required": true,
            "description": "Raw Gmail thread id (hex)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDraftRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Draft"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/email-threads/<threadId>/drafts\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"body\": \"<body>\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email-threads/<threadId>/drafts\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"body\": \"<body>\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"body\": \"<body>\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/email-threads/<threadId>/drafts\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/email-threads/<threadId>/drafts\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"body\": \"<body>\"\n    },\n)\ndata = res.json()"
          }
        ]
      },
      "get": {
        "operationId": "listEmailDrafts",
        "summary": "List your drafts on a thread",
        "tags": [
          "Email"
        ],
        "description": "The key owner’s Gmail drafts sitting on this thread — metadata only (subject, snippet, recipients), never bodies.",
        "x-required-scopes": [
          "email.metadata.read"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "email.metadata.read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "threadId",
            "in": "path",
            "required": true,
            "description": "Raw Gmail thread id (hex)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/email-threads/<threadId>/drafts\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email-threads/<threadId>/drafts\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/email-threads/<threadId>/drafts\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/email-threads/<threadId>/drafts\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email-threads/{threadId}/drafts/{draftId}/send": {
      "post": {
        "operationId": "sendEmailDraft",
        "summary": "Send an existing draft",
        "tags": [
          "Email"
        ],
        "description": "Sends a previously created Gmail draft — the \"approve by sending\" pattern: one key creates drafts (drafts.write), a human or a key with drafts.send releases them. Sending is irreversible; use an `Idempotency-Key`.",
        "x-required-scopes": [
          "drafts.send"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "drafts.send"
            ]
          }
        ],
        "parameters": [
          {
            "name": "threadId",
            "in": "path",
            "required": true,
            "description": "Raw Gmail thread id (hex)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "draftId",
            "in": "path",
            "required": true,
            "description": "Gmail draft id (from create/list drafts)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/email-threads/<threadId>/drafts/<draftId>/send\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email-threads/<threadId>/drafts/<draftId>/send\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/email-threads/<threadId>/drafts/<draftId>/send\", null);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/email-threads/<threadId>/drafts/<draftId>/send\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email-threads/{threadId}/reply": {
      "post": {
        "operationId": "replyToEmailThread",
        "summary": "Reply to a thread (recipients derived from the thread)",
        "tags": [
          "Email"
        ],
        "description": "Sends a reply where you control ONLY the body — recipients are derived server-side from the thread (honouring Reply-To; `mode: reply_all` keeps the To/Cc audience) and cannot be supplied. Content can never leave the thread’s existing audience, making this the safe send scope for agents. Sending is irreversible; use an `Idempotency-Key`.",
        "x-required-scopes": [
          "email.reply"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "email.reply"
            ]
          }
        ],
        "parameters": [
          {
            "name": "threadId",
            "in": "path",
            "required": true,
            "description": "Raw Gmail thread id (hex)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/email-threads/<threadId>/reply\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"body\": \"<body>\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email-threads/<threadId>/reply\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"body\": \"<body>\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"body\": \"<body>\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/email-threads/<threadId>/reply\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/email-threads/<threadId>/reply\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"body\": \"<body>\"\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email/send": {
      "post": {
        "operationId": "sendEmail",
        "summary": "Compose and send an email",
        "tags": [
          "Email"
        ],
        "description": "Sends a new message to arbitrary recipients from the key owner’s mailbox (optionally onto an existing thread via `thread_id`). The highest-risk email scope — grant it deliberately. Sending is irreversible; use an `Idempotency-Key`.",
        "x-required-scopes": [
          "email.send"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "email.send"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendEmailRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/email/send\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"to\": [],\n  \"subject\": \"<subject>\",\n  \"body\": \"<body>\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email/send\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"to\": [],\n  \"subject\": \"<subject>\",\n  \"body\": \"<body>\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"to\": [],\n  \"subject\": \"<subject>\",\n  \"body\": \"<body>\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/email/send\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/email/send\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"to\": [],\n        \"subject\": \"<subject>\",\n        \"body\": \"<body>\"\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/email/drafts": {
      "post": {
        "operationId": "createDraft",
        "summary": "Create a new draft",
        "tags": [
          "Email"
        ],
        "description": "Creates a brand-new Gmail draft (a fresh conversation, not attached to any thread). `to` and `subject` are required; the body may be empty. The draft lives in Gmail — the user can edit, send or delete it there (or an API key with drafts.send can send it). This scope can never send mail. Send an `Idempotency-Key` header so retries don’t create duplicate drafts. To draft a reply onto an existing conversation, use POST /email-threads/{threadId}/drafts instead.",
        "x-required-scopes": [
          "drafts.write"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "drafts.write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateNewDraftRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Draft"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/email/drafts\" \\\n  -H \"Authorization: Bearer sk_live_…\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: <unique-key>\" \\\n  -d '{\n  \"to\": [],\n  \"subject\": \"<subject>\",\n  \"body\": \"<body>\"\n}'"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/email/drafts\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n    \"Content-Type\": \"application/json\",\n    \"Idempotency-Key\": crypto.randomUUID(),\n  },\n  body: JSON.stringify({\n  \"to\": [],\n  \"subject\": \"<subject>\",\n  \"body\": \"<body>\"\n}),\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar content = new StringContent(\"\"\"\n{\n  \"to\": [],\n  \"subject\": \"<subject>\",\n  \"body\": \"<body>\"\n}\n\"\"\", Encoding.UTF8, \"application/json\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/email/drafts\", content);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/email/drafts\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n    json={\n        \"to\": [],\n        \"subject\": \"<subject>\",\n        \"body\": \"<body>\"\n    },\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/ai/analyses": {
      "get": {
        "operationId": "getAiAnalyses",
        "summary": "Read cached AI analyses for a thread",
        "tags": [
          "AI"
        ],
        "description": "The cached AI analyses (status, urgency, sentiment, classification) for a thread, if any. Returns instantly and never enqueues work — use classify-thread to request a fresh analysis.",
        "x-required-scopes": [
          "ai.classify"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "ai.classify"
            ]
          }
        ],
        "parameters": [
          {
            "name": "thread_id",
            "in": "query",
            "required": true,
            "description": "Provider thread id",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/ai/analyses?thread_id=<thread_id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/ai/analyses?thread_id=<thread_id>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/ai/analyses?thread_id=<thread_id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/ai/analyses?thread_id=<thread_id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/ai/classify-thread": {
      "post": {
        "operationId": "classifyThread",
        "summary": "Classify a thread (cached or enqueue)",
        "tags": [
          "AI"
        ],
        "description": "Returns a cached analysis instantly, or 202 with a poll URL (typically 10–30s).",
        "x-required-scopes": [
          "ai.classify"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "ai.classify"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Optional. A unique key (e.g. a UUID) that makes this write safe to retry: a repeated request with the same key replays the original response instead of performing the action twice. Reusing a key with a different request body returns 409. Keys are remembered for 24 hours. Most useful on creates to avoid duplicates.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST \"https://api.sortd.com/v2/ai/classify-thread\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/ai/classify-thread\", {\n  method: \"POST\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.PostAsync(\"https://api.sortd.com/v2/ai/classify-thread\", null);\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.post(\n    \"https://api.sortd.com/v2/ai/classify-thread\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    },
    "/ai/jobs/{id}": {
      "get": {
        "operationId": "getAiJob",
        "summary": "Poll an AI classification job",
        "tags": [
          "AI"
        ],
        "description": "Poll a job returned by classify-thread’s 202 (`poll_url`) until its status is no longer queued/processing, then read the result.",
        "x-required-scopes": [
          "ai.classify"
        ],
        "security": [
          {
            "ApiKeyAuth": [
              "ai.classify"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "AI job id (job_…)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success"
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET \"https://api.sortd.com/v2/ai/jobs/<id>\" \\\n  -H \"Authorization: Bearer sk_live_…\""
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript (fetch)",
            "source": "const res = await fetch(\"https://api.sortd.com/v2/ai/jobs/<id>\", {\n  method: \"GET\",\n  headers: {\n    Authorization: \"Bearer sk_live_…\",\n  },\n});\nconst data = await res.json();"
          },
          {
            "lang": "C#",
            "label": "C#",
            "source": "using var client = new HttpClient();\nclient.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", \"sk_live_…\");\nvar res = await client.GetAsync(\"https://api.sortd.com/v2/ai/jobs/<id>\");\nvar json = await res.Content.ReadAsStringAsync();"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n\nres = requests.get(\n    \"https://api.sortd.com/v2/ai/jobs/<id>\",\n    headers={\"Authorization\": \"Bearer sk_live_…\"},\n)\ndata = res.json()"
          }
        ]
      }
    }
  }
}