{
  "openapi": "3.0.3",
  "info": {
    "title": "Retail Lead Management Read-Only API",
    "version": "3.0",
    "description": "The RLM v3 API is a read-only JSON API over the same data you see in the\nRetail Lead Management web app: leads, tasks, companies, contacts, journal\nentries, attachments and samples.\n\nCompared to the [v1 API](/docs/), v3 offers per-field filtering, sorting,\nfull-text search, pagination, and access to record types v1 does not expose.\nIt writes nothing - every endpoint is a `GET`. If you need to *create* leads,\nuse `POST /api/{apiKey}/leads` on v1.\n\n## Machine-readable spec\n\nThis page is generated from an OpenAPI 3.0.3 document, served alongside it:\n\n| Format | URL |\n| --- | --- |\n| JSON | [`/docs/v3/rlm-v3.json`](/docs/v3/rlm-v3.json) |\n| YAML | [`/docs/v3/rlm-v3.yaml`](/docs/v3/rlm-v3.yaml) |\n\nPoint a client generator, a request validator, or a coding assistant at\neither one rather than at this page. `enum` in them means the API constrains\nthe value; on a response field it is the complete set of values the API will\never send. Values configured per pipeline cannot appear as an `enum` - see\n\"Which values a field can hold\" below for how to read those at runtime.\n\nThe [v1 spec](/docs/) is published the same way, at `/docs/rlm-v1.json`.\n\n## Requesting access\n\nv3 is off by default. Access must be enabled by RLM support, and **three\nseparate things must all be in place** before a request will succeed:\n\n1. v3 access enabled on your **organization**\n2. v3 access enabled on the specific **user** the integration will run as\n3. an active **user API key** issued to that user\n\nEnabling the organization alone is not sufficient, and neither is issuing a\nkey - a user whose own access has not been enabled is rejected even with a\nvalid key belonging to an enabled organization. Contact RLM support with the\nuser account you want the integration to use, and support will enable all\nthree together.\n\nBecause access is per-user, treat the credentials as belonging to that user:\nif they leave, have support issue a new key against a different account\nrather than sharing one.\n\n## Base path\n\n**Every endpoint on this page is prefixed `/api/v3/`, without exception.**\nRead the path printed on each operation as the complete path.\n\nThe one documented RLM endpoint that carries no version in its path is\nv1's `POST /api/{apiKey}/leads`, and it is not on this page - v3 writes\nnothing. See the [v1 API](/docs/) if you need it.\n\n## Authentication\n\nEvery request must send all three headers:\n\n| Header | Value |\n| --- | --- |\n| `X-Organization-Api-Key` | Your organization's API key |\n| `X-User-Email` | The email address of the enabled user |\n| `X-User-Api-Key` | That user's API key |\n\nAll three must match the same user, and the key must be active. Any mismatch\nreturns `401 Unauthorized`.\n\nNote that these are **not** the same headers v1 uses. v1's `X-Api-Key`\nbecomes `X-Organization-Api-Key` here, and v3 adds a per-user secret that v1\nhas no equivalent of.\n\n### What you can see\n\nResults are scoped to what the authenticating user can see in the web app -\ntheir visible pipelines, divisions and locations, and for tasks, the task\nprivacy rules. A narrowly scoped user gets fewer records than a broadly\nscoped one from an identical request. Pick the user whose visibility matches\nthe data set you intend to sync.\n\nv3 draws the same role lines v1 does, from the same six roles:\n\n| Role | Leads |\n| --- | --- |\n| System Admin, Business Admin, Business Manager, Full View | The organization's leads, narrowed to the pipelines, divisions and locations that user may see |\n| Single View | Only the leads assigned to them |\n| Assistant | Only the leads they created that are still pending and unassigned |\n\n**Full View is not a manager role**, even though it sees everything the\nthree above it see. RLM's manager roles are System Admin, Business Admin and\nBusiness Manager; Full View and Single View are salesperson roles. The one\nplace it matters here is `list/deleted` - see that filter.\n\n## Response shape\n\nEvery collection endpoint returns the same envelope:\n\n```json\n{\n  \"data\": [ ... ],\n  \"meta\": { \"matches\": 42, \"total\": 1360 },\n  \"links\": { \"self\": \"...\", \"next\": \"...\" }\n}\n```\n\n- `data` - the records for this page\n- `meta.matches` - how many records matched your query in total\n- `meta.total` - how many records of this type you can see at all, ignoring\n  your query. Useful as a denominator; it is not the size of the result set.\n- `links` - pagination URLs, described below\n\nSingle-record endpoints return `{\"data\": { ... }}`.\n\n### Custom fields, and why their values are objects\n\nLeads, tasks, companies, contacts and samples all carry the custom fields\nconfigured for them in RLM, on top of the standard keys listed in each\nschema. Which ones you get depends on the pipeline and the organization, so\nthe key set is data, not something you can hard-code.\n\n**A custom field's value is an object, not a scalar.** It always has a\n`display` key holding the pre-formatted string the web app shows, plus one\nmore key named after the field's type holding the typed value:\n\n```json\n{\n  \"email\":        {\"display\": \"a@b.com\", \"email\": \"a@b.com\"},\n  \"quote_amount\": {\"display\": null, \"currency\": null},\n  \"opt_in\":       {\"display\": \"No\", \"checkbox\": false},\n  \"location\":     {\"display\": \"Head Office\", \"system_id\": 2966,\n                   \"system\": \"Head Office\"}\n}\n```\n\nRead `display` when you want what the user sees, and the typed key when you\nwant to compare, total or convert the value. The `CustomFieldValue` schema\nlists every type key and which field types produce it.\n\nTwo kinds of field break the pattern and come back as a **bare scalar**\ninstead: fields RLM marks as *locked*, and fields whose name collides with\na standard indexed column - `lead_type`, for example, arrives as\n`\"lead_type\": null` rather than an object. Nothing in the payload tells you\nwhich form you are looking at, so check the type at runtime. Code that\nassumes every custom field is a string will break on the first object it\nmeets, and code that assumes every one is an object will break on the first\nlocked field.\n\nThis is a real difference from v1, where every custom field is a display\nstring.\n\nTo find out which custom fields a pipeline defines, call\n`GET /api/v1/pipeline/{apiKey}` on the v1 API - v3 has no field discovery\nendpoint of its own - and read `pipeline_fields[].column_name`. **v3 keys\ncustom fields by `column_name` and nothing else.** v1 also publishes each\nfield under its `api_field_name` alias when one is set; v3 ignores that\nattribute entirely. A field with `column_name` `deal_name` and\n`api_field_name` `deal_name_api` appears on a v1 lead under both names and\non a v3 lead only as `deal_name`.\n\n`column_name` is not the label RLM shows for the field. A field displayed as\n**Lead Source** arrives here as `source_name`, and the label can be renamed\nwithout the key changing. The same v1 response carries `display_name`\nalongside it, so read both if you need to show a human which field is which\n- and quote the display name, not the key, when asking RLM support about a\nfield.\n\n### Which values a field can hold\n\nFor fields whose value comes from a list, that list also lives on\n`GET /api/v1/pipeline/{apiKey}`:\n\n- `select` and `multiple_select` fields - the permitted values are\n  `pipeline_fields[].field_options` for that field. Mostly **configured per\n  pipeline and editable at any time**, so read them rather than\n  hard-coding, and expect them to differ between two pipelines in the same\n  organization. The `lead_type` and `channel_utm` fields are the exception:\n  RLM locks their values, so those two are safe to code against. Note that\n  `field_options` merges the field's current and discontinued values into\n  one array without distinguishing them.\n- `state` fields - a fixed set of 60 US codes, the same on every pipeline.\n  v1's `LeadCreate.state` lists them in full.\n- a lead's `stage_name` - `pipeline_statuses[].name`, also per pipeline.\n\nThis matters most for `rlm_filters`: filtering on a value that is not in the\nlist is not an error, it simply matches nothing.\n\n### Search index keys on list responses\n\nList endpoints are served from the search index, and two of its internal\nkeys survive into the response:\n\n- `_type` - on every list record, naming the index it came from.\n- `sort` - on company and contact lists only. Those default to ordering by\n  lower-cased name, and only all-numeric sort keys get stripped, so a value\n  like `[1.0, \"acme flooring\"]` stays put.\n\nNeither is part of the API. Ignore them: they carry no information you\ncannot get from the documented fields, they are absent from the\nsingle-record endpoints, and their contents can change without notice.\n\n### Nested paths\n\nMany records are reachable both directly and through a parent - for example\n`GET /api/v3/tasks/{id}` and `GET /api/v3/leads/{lead_id}/tasks/{id}`.\n\n**The parent segment is always enforced.** On list endpoints it restricts the\nresult set; on single-record endpoints the record must genuinely belong to\nthe parent named in the path, and a mismatch returns `404`. The parent itself\nmust also be one of your organization's records, so an unknown or foreign\nparent id is a `404` too.\n\nThis means a nested URL *is* a relationship assertion: if\n`GET /api/v3/leads/{lead_id}/tasks/{id}` returns `200`, that task really is\non that lead. You can rely on it rather than reading the record back and\ncomparing ids.\n\n(Before 2026-08-13 this was true of journals, attachments and samples but not\nof nested tasks, leads or contacts, which resolved by `{id}` alone and\nignored the parent. If you built a client against that behaviour - for\ninstance passing a placeholder parent id - it will now receive `404`.)\n\n### Deleted records\n\nThe single-record endpoints for leads, tasks, companies and contacts return\n**soft-deleted records** as well as live ones. Check the record's `deleted`\nfield rather than assuming a `200` means the record is active. The list\nendpoints exclude deleted records unless you ask for the `deleted` filter.\n\n## Pagination\n\nCollection endpoints return **100 records when you do not ask for a\ndifferent number**. That is a default, not a maximum: `page[size]` is passed\nto the search engine as given, and nothing clamps it, so `page[size]=400`\nreturns 400 records if that many match.\n\nRequest a specific page with `page[number]` and `page[size]`:\n\n```\n/api/v3/leads?page[number]=2&page[size]=50\n```\n\n### The 10,000-record wall\n\n**`page[number]` × `page[size]` may not exceed 10,000.** Cross it and the\nrequest fails with a bare `500` and an empty body - no error envelope, no\nexplanation. It is a hard boundary on how deep you can page and it is not\nconfigurable by the caller: at `page[size]=100`, page 101 does not exist.\n\nMeasured: `page[size]` of 100, 500, 9999 and 10000 all answer `200`;\n`page[size]=10001` answers `500`, and so does\n`page[number]=200&page[size]=100`.\n\n**A request past the wall does not fail quickly.** It is not rejected up\nfront - it runs, and fails at the end. An in-window request answers in well\nunder a second; a past-the-wall one can take **minutes** to come back with\nits `500`. Set a client-side timeout accordingly, and do not read a slow\nresponse as \"still working, give it longer\".\n\n**Do not retry one unchanged.** A past-the-wall request costs far more to\nfail than an ordinary request costs to succeed, on your side and on RLM's,\nand a scheduled job that re-issues one on a timer degrades service for your\nown integration and for other users. Treat a `500` from a paged request as\n\"my window is too big\": fix the query before sending it again.\n\nThe shape to avoid is a page number that climbs on a schedule until it\ncrosses the wall - a sync that adds a page each run and never re-checks the\nlimit gets there on its own.\n\nPaging deeper is not the way around it. **Narrow the query instead**, so\nthat each result set is comfortably under 10,000, and move the *filter*\nforward rather than the page number:\n\n1. Pick a window on `created_at` or `updated_at` - a few weeks for a busy\n   resource, longer for a quiet one.\n2. Check `meta.matches` on the first page. If it is near 10,000, halve the\n   window and start again.\n3. Page through that window, then advance the window and repeat.\n\nStarting points that work in practice: 30-day windows on `created_at` for\nleads at `page[size]=400`, which lands around 1,000 matches per window, and\n1-day to 5-day windows for contacts and companies, which are higher-volume.\nTune from there against your own `meta.matches`.\n\nSizing the window this way also keeps you clear of the rate limit. Prefer\nseveral modest pages to one very large one: a single huge page is one\nrequest, but it is a disproportionately expensive one.\n\n`links` is **always** present on a collection response and always carries\n`self`. It carries `first`, `prev`, `next` and `last` as well wherever those\npages exist: `first` and `prev` are omitted on page 1, `next` and `last` on\nthe final page. Follow `next` until it is absent.\n\n**This does not depend on your having asked for a page.** A request with no\npagination parameters is served as page 1 of size 100, so a query matching\nmore than 100 records comes back with `next` and `last` already filled in,\nand following `next` walks the whole set. Measured on a 123-record query\nwith no parameters: 100 records, and\n`links.next = ...?page[number]=2&page[size]=100`.\n\nPaginate explicitly anyway when you are syncing. Following `next` from an\nunparameterised request does work, but the page size is then whatever the\ndefault happens to be, and a default that moves shifts every page boundary\nunderneath a resumable sync.\n\n`limit` is accepted as an alias for `page[size]`.\n\n## Filtering\n\nThree independent mechanisms, which combine:\n\n- `{resource}/list/{filter}` - a coarse status filter built into the path,\n  e.g. `/api/v3/leads/list/closed`. Values differ per resource and are\n  documented on each endpoint.\n- `query` - full-text search across the record.\n- `rlm_filters[...]` - per-field filtering, described below.\n\n### Per-field filters\n\n`rlm_filters` is a nested query parameter keyed by field name:\n\n```\n?rlm_filters[stage_name]=Sold\n```\n\nField names are the keys that appear on the records in `data`, including the\ncustom fields configured on your pipelines. To discover the field names and\npermitted values for a pipeline, call\n`GET /api/v1/pipeline/{apiKey}` on the v1 API - v3 does not expose a field\ndiscovery endpoint of its own - and filter on\n`pipeline_fields[].column_name`. That is the only name v3 knows a custom\nfield by; the `api_field_name` alias works on v1 but is ignored here, and\nbecause unrecognised field names are dropped silently, filtering on one\nreturns unfiltered results rather than an error.\n\nPass an **array** to match any of several values (OR):\n\n```\n?rlm_filters[stage_name][]=Sold&rlm_filters[stage_name][]=Completed\n```\n\nDate and number fields accept **range operators** - `gt`, `gte`, `lt`, `lte`\nand `eq`:\n\n```\n?rlm_filters[created_at][gte]=2026-08-01&rlm_filters[created_at][lt]=2026-09-01\n```\n\nAny field accepts an **emptiness test**. `empty=true` matches records where\nthe field is null, absent or an empty string; `empty=false` matches records\nwhere it has a value:\n\n```\n?rlm_filters[email][empty]=false\n```\n\nFilters on different fields are ANDed together. An unrecognised field name is\nsilently dropped rather than erroring, so a typo returns unfiltered results -\ncheck your result counts against `meta.matches` when adding a filter.\n\n## Sorting\n\n`rlm_sorting` takes a direction and a priority per field, letting you sort by\nseveral fields at once - `priority` orders the sort keys, lowest first:\n\n```\n?rlm_sorting[updated_at][direction]=desc&rlm_sorting[updated_at][priority]=0\n```\n\nOmit it and each resource applies its own default ordering, documented per\nendpoint.\n\n## Rate limits\n\nv3 is limited to **2 requests per second**, measured over a 5 second window.\nThis is deliberately much tighter than v1 because v3 queries are more\nexpensive, and it is enforced **both per source IP and per credential set**,\nso spreading requests across machines does not raise the ceiling.\n\nExceeding it returns `429 Too Many Requests` with a `Retry-After` header\ngiving the seconds to wait. Back off and retry rather than retrying\nimmediately; sustained overage will keep you throttled.\n\nFor a **one-off** bulk extract you are running yourself, a scheduled export\nis kinder than paging the whole data set. It is **not** an option for an\nunattended integration: an export is delivered to the requesting user out of\nband and the endpoint answers with a queue acknowledgement rather than a\nfile, so a scheduled job has nothing to collect. An automated sync should\npage an `updated_at` window instead - see Incremental syncing below - and\nshould not re-read the whole data set on every run.\n\n## Incremental syncing\n\nFilter on `updated_at` and page through the results, oldest first:\n\n```\n/api/v3/leads/list/all\n    ?rlm_filters[updated_at][gte]=2026-08-05T14:30:00Z\n    &rlm_sorting[updated_at][direction]=asc\n    &rlm_sorting[updated_at][priority]=0\n    &page[number]=1&page[size]=100\n```\n\nUse `list/all` rather than the default `open` so that leads which closed\nsince your last sync are not silently missing from the result.\n\n**Bound the window at both ends once you are backfilling.** An open-ended\n`gte` over a large history matches far more than 10,000 records, and the sync\nthen dies on the wall described under Pagination the moment it reaches page\n101. Add an `lte`, keep `meta.matches` per window under a few thousand, and\nadvance the window:\n\n```\n/api/v3/leads/list/all\n    ?rlm_filters[created_at][gte]=2026-07-01\n    &rlm_filters[created_at][lte]=2026-07-31\n    &rlm_sorting[created_at][direction]=asc\n    &rlm_sorting[created_at][priority]=0\n    &page[number]=1&page[size]=400\n```\n\nSteady-state polling of a short window rarely reaches the wall; it is the\ninitial backfill that does.\n",
    "contact": {
      "name": "RLM Support"
    }
  },
  "servers": [
    {
      "url": "https://app.retailleadmanagement.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Leads",
      "description": "A lead is a sales opportunity moving through a pipeline. Leads carry the\ncustom fields configured for their pipeline as well as the standard ones,\nso the exact key set varies between organizations.\n"
    },
    {
      "name": "Tasks",
      "description": "Scheduled work attached to a lead, company or contact. Task visibility\nadditionally respects task privacy, so a restricted user sees fewer tasks\nthan they see leads.\n"
    },
    {
      "name": "Companies",
      "description": "Companies are part of the Customer Management Module. On accounts without\nthat module every company endpoint - list and single-record alike -\nanswers `403`.\n"
    },
    {
      "name": "Contacts",
      "description": "People, optionally associated with a company. Contacts are also part of\nthe Customer Management Module, with the same `403` on accounts that do\nnot have it.\n"
    },
    {
      "name": "Samples",
      "description": "Physical product samples loaned to a customer during a sale, tracked by\nstatus. Also reachable under `/api/v3/pipeline_samples`.\n"
    },
    {
      "name": "Journals",
      "description": "Journal entries are the activity history of a record. They are always\naccessed through their parent record.\n"
    },
    {
      "name": "Attachments",
      "description": "Attachment metadata for a record. Always accessed through the parent\nrecord. The response describes the files and links to them; it does not\nembed their contents.\n"
    },
    {
      "name": "Exports",
      "description": "Exports are generated asynchronously and delivered to the requesting user\nout of band. The endpoint returns immediately with a queue acknowledgement,\nnot a file.\n"
    }
  ],
  "security": [
    {
      "OrganizationApiKey": [

      ],
      "UserEmail": [

      ],
      "UserApiKey": [

      ]
    }
  ],
  "paths": {
    "/api/v3/leads": {
      "get": {
        "tags": [
          "Leads"
        ],
        "operationId": "listLeads",
        "summary": "List leads",
        "description": "Returns open leads by default, most recently updated first. Use\n`/api/v3/leads/list/{filter}` to select a different set.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/LeadList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/leads/list/{filter}": {
      "get": {
        "tags": [
          "Leads"
        ],
        "operationId": "listLeadsFiltered",
        "summary": "List leads by status",
        "parameters": [
          {
            "name": "filter",
            "in": "path",
            "required": true,
            "description": "- `open` - leads still moving through the pipeline\n- `closed` - leads in a closed status\n- `all` - open and closed\n- `deleted` - soft-deleted leads. Only RLM's three **manager**\n  roles - System Admin, Business Admin, Business Manager - see the\n  whole organization's; everyone else sees only their own.\n  **This includes Full View**, which is a salesperson role and gets\n  just its own deleted leads here, despite seeing the whole\n  organization under every other filter. See \"What you can see\".\n\nAn unrecognised value falls back to `open`.\n",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "closed",
                "all",
                "deleted"
              ]
            },
            "example": "all"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/LeadList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/leads/export": {
      "get": {
        "tags": [
          "Leads",
          "Exports"
        ],
        "operationId": "exportAllLeads",
        "summary": "Queue an export of all leads",
        "description": "Queues a spreadsheet export of **all** open leads and returns\nimmediately. The file is delivered to the authenticating user out of\nband once generated - it is **not** returned in the response body.\n\nThis is the same operation as\n`/api/v3/leads/list/{filter}/export` with `{filter}`\nset to `open`, which is the default when the segment is\nabsent.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          },
          {
            "$ref": "#/components/parameters/RlmFields"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ExportQueued"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ExportFailed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/leads/list/{filter}/export": {
      "get": {
        "tags": [
          "Leads",
          "Exports"
        ],
        "operationId": "exportLeads",
        "summary": "Queue a lead export",
        "description": "Queues a spreadsheet export of the leads matching this request and\nreturns immediately. The file is delivered to the authenticating user out\nof band once generated - it is **not** returned in the response body.\n\nAccepts the same filtering, search and sorting parameters as the\ncorresponding list endpoint, so you can export exactly the set you would\notherwise page through. Use `all` as the `{filter}` to export\neverything.\n",
        "parameters": [
          {
            "name": "filter",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "closed",
                "all",
                "deleted"
              ]
            },
            "example": "all"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          },
          {
            "$ref": "#/components/parameters/RlmFields"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ExportQueued"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ExportFailed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/leads/{id}": {
      "get": {
        "tags": [
          "Leads"
        ],
        "operationId": "getLead",
        "summary": "Get a lead",
        "description": "Returns one lead, including soft-deleted ones. The payload carries the\nsame keys as an entry in the list response - this one is rendered from\nthe record itself rather than the search index, but nothing is added or\nleft out because of that.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The lead.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Lead"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/leads/{lead_id}/tasks": {
      "get": {
        "tags": [
          "Leads",
          "Tasks"
        ],
        "operationId": "listLeadTasks",
        "summary": "List a lead's tasks",
        "parameters": [
          {
            "$ref": "#/components/parameters/LeadId"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TaskList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/leads/{lead_id}/tasks/{id}": {
      "get": {
        "tags": [
          "Leads",
          "Tasks"
        ],
        "operationId": "getLeadTask",
        "summary": "Get a task through its lead",
        "description": "Returns the task with the given `{id}`.\n\nThe task must belong to the lead named by `{lead_id}`; if it does not,\nor if that lead is not one of your organization's, the response is\n`404`. A `200` here is therefore proof of the relationship.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/LeadId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The task.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Task"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/leads/{lead_id}/pipeline_samples": {
      "get": {
        "tags": [
          "Leads",
          "Samples"
        ],
        "operationId": "listLeadSamples",
        "summary": "List a lead's samples",
        "parameters": [
          {
            "$ref": "#/components/parameters/LeadId"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SampleList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/leads/{lead_id}/pipeline_samples/{id}": {
      "get": {
        "tags": [
          "Leads",
          "Samples"
        ],
        "operationId": "getLeadSample",
        "summary": "Get a sample through its lead",
        "description": "Returns the sample with the given `{id}`, restricted to samples on the\nlead identified by `{lead_id}`. Unlike the nested task and lead lookups,\nthis one **does** honour the parent segment: a sample belonging to a\ndifferent lead returns `404`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/LeadId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The sample.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Sample"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/leads/{lead_id}/journals": {
      "get": {
        "tags": [
          "Leads",
          "Journals"
        ],
        "operationId": "listLeadJournals",
        "summary": "List a lead's journal entries",
        "description": "Returns the lead's activity history. Entries are enriched with the\ndisplay names of the related category, task type and users, alongside\ntheir ids. That enrichment happens only here - fetching a single entry\nby id returns the ids without the names.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/LeadId"
          },
          {
            "$ref": "#/components/parameters/JournalDescription"
          },
          {
            "$ref": "#/components/parameters/JournalSortBy"
          },
          {
            "$ref": "#/components/parameters/JournalCategoryId"
          },
          {
            "$ref": "#/components/parameters/JournalCreatedById"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/JournalList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/leads/{lead_id}/journals/{id}": {
      "get": {
        "tags": [
          "Leads",
          "Journals"
        ],
        "operationId": "getLeadJournal",
        "summary": "Get a journal entry",
        "parameters": [
          {
            "$ref": "#/components/parameters/LeadId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The journal entry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Journal"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/leads/{lead_id}/attachments": {
      "get": {
        "tags": [
          "Leads",
          "Attachments"
        ],
        "operationId": "listLeadAttachments",
        "summary": "List a lead's attachments",
        "description": "Returns attachment metadata and download links. This endpoint is not\npaginated - it returns every attachment on the record.\n\nFor organizations with a BMS integration active, an additional `edocs`\nkey lists electronic documents held in the connected system. Those\nentries look like attachments but their links are session-authenticated\nRLM URLs, not direct file links - read the `Edoc` schema before\nplanning to fetch them.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/LeadId"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/AttachmentList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/leads/{lead_id}/attachments/{id}": {
      "get": {
        "tags": [
          "Leads",
          "Attachments"
        ],
        "operationId": "getLeadAttachment",
        "summary": "Get an attachment",
        "parameters": [
          {
            "$ref": "#/components/parameters/LeadId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The attachment metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Attachment"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/tasks": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "operationId": "listTasks",
        "summary": "List tasks",
        "description": "Returns incomplete tasks by default, most recently created first.\nRespects task privacy - private tasks belonging to other users are\nexcluded.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TaskList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/tasks/list/{filter}": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "operationId": "listTasksFiltered",
        "summary": "List tasks by status",
        "parameters": [
          {
            "name": "filter",
            "in": "path",
            "required": true,
            "description": "- `incomplete` - not yet completed\n- `completed` - completed\n- `deleted` - soft-deleted\n- `all` - no status restriction\n\nAn unrecognised value falls back to `all`. Note this differs from\nleads, where the fallback is `open`.\n",
            "schema": {
              "type": "string",
              "enum": [
                "incomplete",
                "completed",
                "deleted",
                "all"
              ]
            },
            "example": "incomplete"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TaskList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/tasks/export": {
      "get": {
        "tags": [
          "Tasks",
          "Exports"
        ],
        "operationId": "exportAllTasks",
        "summary": "Queue an export of all tasks",
        "description": "Queues a spreadsheet export of **all** tasks and returns\nimmediately. The file is delivered to the authenticating user out of\nband once generated - it is **not** returned in the response body.\n\nThis is the same operation as\n`/api/v3/tasks/list/{filter}/export` with `{filter}`\nset to `all`, which is the default when the segment is\nabsent.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          },
          {
            "$ref": "#/components/parameters/RlmFields"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ExportQueued"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ExportFailed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/tasks/list/{filter}/export": {
      "get": {
        "tags": [
          "Tasks",
          "Exports"
        ],
        "operationId": "exportTasks",
        "summary": "Queue a task export",
        "description": "Queues a spreadsheet export of the matching tasks. See\n`/api/v3/leads/list/{filter}/export` for how exports are delivered.\n",
        "parameters": [
          {
            "name": "filter",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "incomplete",
                "completed",
                "deleted",
                "all"
              ]
            },
            "example": "all"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          },
          {
            "$ref": "#/components/parameters/RlmFields"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ExportQueued"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ExportFailed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/tasks/{id}": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "operationId": "getTask",
        "summary": "Get a task",
        "parameters": [
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The task.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Task"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/tasks/{task_id}/journals": {
      "get": {
        "tags": [
          "Tasks",
          "Journals"
        ],
        "operationId": "listTaskJournals",
        "summary": "List a task's journal entries",
        "parameters": [
          {
            "$ref": "#/components/parameters/TaskId"
          },
          {
            "$ref": "#/components/parameters/JournalDescription"
          },
          {
            "$ref": "#/components/parameters/JournalSortBy"
          },
          {
            "$ref": "#/components/parameters/JournalCategoryId"
          },
          {
            "$ref": "#/components/parameters/JournalCreatedById"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/JournalList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/tasks/{task_id}/journals/{id}": {
      "get": {
        "tags": [
          "Tasks",
          "Journals"
        ],
        "operationId": "getTaskJournal",
        "summary": "Get a task's journal entry",
        "description": "Returns the journal entry with the given `{id}`, restricted to entries\non the task identified by `{task_id}`. An entry belonging to a different\nrecord returns `404`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/TaskId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The journal entry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Journal"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/tasks/{task_id}/attachments": {
      "get": {
        "tags": [
          "Tasks",
          "Attachments"
        ],
        "operationId": "listTaskAttachments",
        "summary": "List a task's attachments",
        "parameters": [
          {
            "$ref": "#/components/parameters/TaskId"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/AttachmentList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/tasks/{task_id}/attachments/{id}": {
      "get": {
        "tags": [
          "Tasks",
          "Attachments"
        ],
        "operationId": "getTaskAttachment",
        "summary": "Get a task's attachment",
        "description": "Returns the attachment with the given `{id}`, restricted to attachments\non the task identified by `{task_id}`. An attachment belonging to a\ndifferent record returns `404`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/TaskId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The attachment.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Attachment"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies": {
      "get": {
        "tags": [
          "Companies"
        ],
        "operationId": "listCompanies",
        "summary": "List companies",
        "description": "Returns companies in name order.\n\nCompanies are part of the Customer Management Module. Without it the\nrequest is refused outright with `403` - you do not get an empty list,\nso treat a `403` here as \"not licensed\" rather than \"no companies\".\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/CompanyList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/list/{filter}": {
      "get": {
        "tags": [
          "Companies"
        ],
        "operationId": "listCompaniesFiltered",
        "summary": "List companies by status",
        "parameters": [
          {
            "$ref": "#/components/parameters/AllOrDeletedFilter"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/CompanyList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/export": {
      "get": {
        "tags": [
          "Companies",
          "Exports"
        ],
        "operationId": "exportAllCompanies",
        "summary": "Queue an export of all companies",
        "description": "Queues a spreadsheet export of **all** companies and returns\nimmediately. The file is delivered to the authenticating user out of\nband once generated - it is **not** returned in the response body.\n\nThis is the same operation as\n`/api/v3/companies/list/{filter}/export` with `{filter}`\nset to `all`, which is the default when the segment is\nabsent.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          },
          {
            "$ref": "#/components/parameters/RlmFields"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ExportQueued"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ExportFailed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/list/{filter}/export": {
      "get": {
        "tags": [
          "Companies",
          "Exports"
        ],
        "operationId": "exportCompanies",
        "summary": "Queue a company export",
        "parameters": [
          {
            "$ref": "#/components/parameters/AllOrDeletedFilter"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          },
          {
            "$ref": "#/components/parameters/RlmFields"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ExportQueued"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ExportFailed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/{id}": {
      "get": {
        "tags": [
          "Companies"
        ],
        "operationId": "getCompany",
        "summary": "Get a company",
        "description": "Requires the Customer Management Module. Accounts without it get a\n`403` with a JSON error body, like every other error on v3.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The company.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Company"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/{company_id}/leads": {
      "get": {
        "tags": [
          "Companies",
          "Leads"
        ],
        "operationId": "listCompanyLeads",
        "summary": "List a company's leads",
        "parameters": [
          {
            "$ref": "#/components/parameters/CompanyId"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/LeadList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/{company_id}/leads/{id}": {
      "get": {
        "tags": [
          "Companies",
          "Leads"
        ],
        "operationId": "getCompanyLead",
        "summary": "Get a lead through its company",
        "description": "Returns the lead with the given `{id}`.\n\nThe lead must belong to the company named by `{company_id}`; if it does\nnot, or if that company is not one of your organization's, the response\nis `404`. A `200` here is therefore proof of the relationship.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/CompanyId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The lead.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Lead"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/{company_id}/tasks": {
      "get": {
        "tags": [
          "Companies",
          "Tasks"
        ],
        "operationId": "listCompanyTasks",
        "summary": "List a company's tasks",
        "parameters": [
          {
            "$ref": "#/components/parameters/CompanyId"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TaskList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/{company_id}/tasks/{id}": {
      "get": {
        "tags": [
          "Companies",
          "Tasks"
        ],
        "operationId": "getCompanyTask",
        "summary": "Get a task through its company",
        "description": "Returns the task with the given `{id}`.\n\nThe task must belong to the company named by `{company_id}`; if it does\nnot, or if that company is not one of your organization's, the response\nis `404`. A `200` here is therefore proof of the relationship.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/CompanyId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The task.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Task"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/{company_id}/contacts": {
      "get": {
        "tags": [
          "Companies",
          "Contacts"
        ],
        "operationId": "listCompanyContacts",
        "summary": "List a company's contacts",
        "parameters": [
          {
            "$ref": "#/components/parameters/CompanyId"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/MatchFields"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ContactList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/{company_id}/contacts/{id}": {
      "get": {
        "tags": [
          "Companies",
          "Contacts"
        ],
        "operationId": "getCompanyContact",
        "summary": "Get a contact through its company",
        "description": "Returns the contact with the given `{id}`.\n\nThe contact must be associated with the company named by\n`{company_id}`; if it is not, or if that company is not one of your\norganization's, the response is `404`. A `200` here is therefore proof\nof the relationship.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/CompanyId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The contact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/{company_id}/journals": {
      "get": {
        "tags": [
          "Companies",
          "Journals"
        ],
        "operationId": "listCompanyJournals",
        "summary": "List a company's journal entries",
        "parameters": [
          {
            "$ref": "#/components/parameters/CompanyId"
          },
          {
            "$ref": "#/components/parameters/JournalDescription"
          },
          {
            "$ref": "#/components/parameters/JournalSortBy"
          },
          {
            "$ref": "#/components/parameters/JournalCategoryId"
          },
          {
            "$ref": "#/components/parameters/JournalCreatedById"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/JournalList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/{company_id}/journals/{id}": {
      "get": {
        "tags": [
          "Companies",
          "Journals"
        ],
        "operationId": "getCompanyJournal",
        "summary": "Get a company's journal entry",
        "description": "Returns the journal entry with the given `{id}`, restricted to entries\non the company identified by `{company_id}`. An entry belonging to a\ndifferent record returns `404`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/CompanyId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The journal entry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Journal"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/{company_id}/attachments": {
      "get": {
        "tags": [
          "Companies",
          "Attachments"
        ],
        "operationId": "listCompanyAttachments",
        "summary": "List a company's attachments",
        "parameters": [
          {
            "$ref": "#/components/parameters/CompanyId"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/AttachmentList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/companies/{company_id}/attachments/{id}": {
      "get": {
        "tags": [
          "Companies",
          "Attachments"
        ],
        "operationId": "getCompanyAttachment",
        "summary": "Get a company's attachment",
        "description": "Returns the attachment with the given `{id}`, restricted to attachments\non the company identified by `{company_id}`. An attachment belonging to\na different record returns `404`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/CompanyId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The attachment.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Attachment"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "listContacts",
        "summary": "List contacts",
        "description": "Returns contacts in last-name, first-name order.\n\nContacts are part of the Customer Management Module. Without it the\nrequest is refused outright with `403` - you do not get an empty list,\nso treat a `403` here as \"not licensed\" rather than \"no contacts\".\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/MatchFields"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ContactList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts/list/{filter}": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "listContactsFiltered",
        "summary": "List contacts by status",
        "parameters": [
          {
            "$ref": "#/components/parameters/AllOrDeletedFilter"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/MatchFields"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ContactList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts/export": {
      "get": {
        "tags": [
          "Contacts",
          "Exports"
        ],
        "operationId": "exportAllContacts",
        "summary": "Queue an export of all contacts",
        "description": "Queues a spreadsheet export of **all** contacts and returns\nimmediately. The file is delivered to the authenticating user out of\nband once generated - it is **not** returned in the response body.\n\nThis is the same operation as\n`/api/v3/contacts/list/{filter}/export` with `{filter}`\nset to `all`, which is the default when the segment is\nabsent.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/MatchFields"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          },
          {
            "$ref": "#/components/parameters/RlmFields"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ExportQueued"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ExportFailed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts/list/{filter}/export": {
      "get": {
        "tags": [
          "Contacts",
          "Exports"
        ],
        "operationId": "exportContacts",
        "summary": "Queue a contact export",
        "parameters": [
          {
            "$ref": "#/components/parameters/AllOrDeletedFilter"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/MatchFields"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          },
          {
            "$ref": "#/components/parameters/RlmFields"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ExportQueued"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ExportFailed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts/{id}": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "operationId": "getContact",
        "summary": "Get a contact",
        "description": "Requires the Customer Management Module, exactly as\n`GET /api/v3/companies/{id}` does. Accounts without it get a `403`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The contact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts/{contact_id}/leads": {
      "get": {
        "tags": [
          "Contacts",
          "Leads"
        ],
        "operationId": "listContactLeads",
        "summary": "List a contact's leads",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/LeadList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts/{contact_id}/leads/{id}": {
      "get": {
        "tags": [
          "Contacts",
          "Leads"
        ],
        "operationId": "getContactLead",
        "summary": "Get a lead through its contact",
        "description": "Returns the lead with the given `{id}`.\n\nThe lead must belong to the contact named by `{contact_id}`; if it does\nnot, or if that contact is not one of your organization's, the response\nis `404`. A `200` here is therefore proof of the relationship.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The lead.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Lead"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts/{contact_id}/tasks": {
      "get": {
        "tags": [
          "Contacts",
          "Tasks"
        ],
        "operationId": "listContactTasks",
        "summary": "List a contact's tasks",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TaskList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts/{contact_id}/tasks/{id}": {
      "get": {
        "tags": [
          "Contacts",
          "Tasks"
        ],
        "operationId": "getContactTask",
        "summary": "Get a task through its contact",
        "description": "Returns the task with the given `{id}`.\n\nThe task must belong to the contact named by `{contact_id}`; if it does\nnot, or if that contact is not one of your organization's, the response\nis `404`. A `200` here is therefore proof of the relationship.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The task.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Task"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts/{contact_id}/journals": {
      "get": {
        "tags": [
          "Contacts",
          "Journals"
        ],
        "operationId": "listContactJournals",
        "summary": "List a contact's journal entries",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          },
          {
            "$ref": "#/components/parameters/JournalDescription"
          },
          {
            "$ref": "#/components/parameters/JournalSortBy"
          },
          {
            "$ref": "#/components/parameters/JournalCategoryId"
          },
          {
            "$ref": "#/components/parameters/JournalCreatedById"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/JournalList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts/{contact_id}/journals/{id}": {
      "get": {
        "tags": [
          "Contacts",
          "Journals"
        ],
        "operationId": "getContactJournal",
        "summary": "Get a contact's journal entry",
        "description": "Returns the journal entry with the given `{id}`, restricted to entries\non the contact identified by `{contact_id}`. An entry belonging to a\ndifferent record returns `404`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The journal entry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Journal"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts/{contact_id}/attachments": {
      "get": {
        "tags": [
          "Contacts",
          "Attachments"
        ],
        "operationId": "listContactAttachments",
        "summary": "List a contact's attachments",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/AttachmentList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/contacts/{contact_id}/attachments/{id}": {
      "get": {
        "tags": [
          "Contacts",
          "Attachments"
        ],
        "operationId": "getContactAttachment",
        "summary": "Get a contact's attachment",
        "description": "Returns the attachment with the given `{id}`, restricted to attachments\non the contact identified by `{contact_id}`. An attachment belonging to\na different record returns `404`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/ContactId"
          },
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The attachment.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Attachment"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/samples": {
      "get": {
        "tags": [
          "Samples"
        ],
        "operationId": "listSamples",
        "summary": "List samples",
        "description": "Returns samples currently with a customer by default. Samples track\nphysical product samples loaned out during a sale.\n\n`/api/v3/pipeline_samples` is a complete alias for the sample paths -\nthe list, the single record and the export all work under either\nspelling. `/api/v3/samples` is the shorter form and the one used\nthroughout these docs.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SampleList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/samples/list/{filter}": {
      "get": {
        "tags": [
          "Samples"
        ],
        "operationId": "listSamplesFiltered",
        "summary": "List samples by status",
        "parameters": [
          {
            "name": "filter",
            "in": "path",
            "required": true,
            "description": "- `with-customer` - currently loaned out\n- `on-order` - ordered but not yet received\n- `returned` - returned by the customer\n- `all` - no status restriction\n\nAn unrecognised value falls back to `all`.\n",
            "schema": {
              "type": "string",
              "enum": [
                "with-customer",
                "on-order",
                "returned",
                "all"
              ]
            },
            "example": "with-customer"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/PageNumber"
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SampleList"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/samples/export": {
      "get": {
        "tags": [
          "Samples",
          "Exports"
        ],
        "operationId": "exportSamples",
        "summary": "Queue a sample export",
        "description": "Queues a spreadsheet export of samples. See\n`/api/v3/leads/list/{filter}/export` for how exports are delivered.\n\n`/api/v3/pipeline_samples/export` is an equivalent alias.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          },
          {
            "$ref": "#/components/parameters/RlmFields"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ExportQueued"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ExportFailed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/samples/list/{filter}/export": {
      "get": {
        "tags": [
          "Samples",
          "Exports"
        ],
        "operationId": "exportSamplesFiltered",
        "summary": "Queue a sample export for one status",
        "description": "Queues a spreadsheet export of the samples matching `{filter}`. Same\nbehaviour as `/api/v3/samples/export`, restricted to one status.\n",
        "parameters": [
          {
            "name": "filter",
            "in": "path",
            "required": true,
            "description": "- `with-customer` - currently loaned out\n- `on-order` - ordered but not yet received\n- `returned` - returned by the customer\n- `all` - no status restriction\n\nAn unrecognised value falls back to `all`.\n",
            "schema": {
              "type": "string",
              "enum": [
                "with-customer",
                "on-order",
                "returned",
                "all"
              ]
            },
            "example": "with-customer"
          },
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/RlmFilters"
          },
          {
            "$ref": "#/components/parameters/RlmSorting"
          },
          {
            "$ref": "#/components/parameters/RlmFields"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ExportQueued"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ExportFailed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/v3/samples/{id}": {
      "get": {
        "tags": [
          "Samples"
        ],
        "operationId": "getSample",
        "summary": "Get a sample",
        "description": "`/api/v3/pipeline_samples/{id}` is an equivalent alias.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/RecordId"
          }
        ],
        "responses": {
          "200": {
            "description": "The sample.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Sample"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "OrganizationApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Organization-Api-Key",
        "description": "Your organization's API key. Note this is a different header name from\nv1, which calls the same value `X-Api-Key`.\n\n32 hexadecimal characters. Example shape (a placeholder, not a usable\nkey): `0123456789abcdef0123456789abcdef`\n"
      },
      "UserEmail": {
        "type": "apiKey",
        "in": "header",
        "name": "X-User-Email",
        "description": "The email address of the user the integration runs as. Must have v3\naccess enabled.\n\nExample shape: `integration-user@example.com`\n"
      },
      "UserApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-User-Api-Key",
        "description": "That user's API key. Must be active and belong to the user named in\n`X-User-Email`. This is a per-user secret that v1 has no equivalent of.\n\n48 URL-safe characters. Example shape (a placeholder, not a usable key):\n`AAAABBBBCCCCDDDDEEEEFFFF000011112222333344445555`\n"
      }
    },
    "parameters": {
      "RecordId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The RLM record id.",
        "schema": {
          "type": "integer"
        },
        "example": 1234567
      },
      "LeadId": {
        "name": "lead_id",
        "in": "path",
        "required": true,
        "description": "The RLM lead id.",
        "schema": {
          "type": "integer"
        },
        "example": 1234567
      },
      "TaskId": {
        "name": "task_id",
        "in": "path",
        "required": true,
        "description": "The RLM task id.",
        "schema": {
          "type": "integer"
        },
        "example": 1234567
      },
      "CompanyId": {
        "name": "company_id",
        "in": "path",
        "required": true,
        "description": "The RLM company id.",
        "schema": {
          "type": "integer"
        },
        "example": 1234567
      },
      "ContactId": {
        "name": "contact_id",
        "in": "path",
        "required": true,
        "description": "The RLM contact id.",
        "schema": {
          "type": "integer"
        },
        "example": 1234567
      },
      "AllOrDeletedFilter": {
        "name": "filter",
        "in": "path",
        "required": true,
        "description": "- `all` - active records\n- `deleted` - soft-deleted records\n\nAn unrecognised value falls back to `all`.\n",
        "schema": {
          "type": "string",
          "enum": [
            "all",
            "deleted"
          ]
        },
        "example": "all"
      },
      "Query": {
        "name": "query",
        "in": "query",
        "required": false,
        "description": "Full-text search across the record. Omit it, or pass `*`, to match\neverything.\n",
        "schema": {
          "type": "string"
        },
        "example": "smith"
      },
      "PageNumber": {
        "name": "page[number]",
        "in": "query",
        "required": false,
        "description": "Which page to return, starting at 1. Must be sent together with\n`page[size]` for pagination links to be generated.\n",
        "schema": {
          "type": "integer",
          "minimum": 1
        },
        "example": 1
      },
      "PageSize": {
        "name": "page[size]",
        "in": "query",
        "required": false,
        "description": "Records per page. Defaults to 100 when omitted.",
        "schema": {
          "type": "integer"
        },
        "example": 100
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Alias for `page[size]`. Defaults to 100. Supplying this alone caps the\nresult set but does not produce pagination links.\n",
        "schema": {
          "type": "integer"
        },
        "example": 100
      },
      "RlmFilters": {
        "name": "rlm_filters",
        "in": "query",
        "required": false,
        "style": "deepObject",
        "explode": true,
        "description": "Per-field filters, keyed by field name - for example\n`rlm_filters[stage_name]=Sold`.\n\nValues may be a scalar, an array (matches any), or an object using the\n`gt` / `gte` / `lt` / `lte` / `eq` range operators on date and number\nfields, or `empty` on any field. See the Filtering section of the\nintroduction for the full syntax. Unrecognised field names are ignored.\n",
        "schema": {
          "type": "object",
          "additionalProperties": true
        }
      },
      "RlmSorting": {
        "name": "rlm_sorting",
        "in": "query",
        "required": false,
        "style": "deepObject",
        "explode": true,
        "description": "Multi-field sorting, keyed by field name, each with a `direction`\n(`asc` or `desc`) and a `priority` (lower sorts first) - for example\n`rlm_sorting[updated_at][direction]=desc&rlm_sorting[updated_at][priority]=0`.\n\n**Which names are accepted is computed per request, so this document\ndoes not print a list.** The server builds the permitted set from the\nsearch index for the record type being listed: every indexed field, plus\nevery multi-field beneath one. That index carries each organization's own\ncustom fields, so the set differs between accounts and grows when an\naccount adds a field. Any list printed here would be wrong for most\ncallers and would go stale for the rest.\n\n**What you can rely on: every property of the objects this endpoint\nreturns is a sortable name.** That is the discovery path - read one page\nand take the keys.\n\n**Sortable and filterable are not the same set, and sortable is the\nsmaller one.** What you may sort on is the *index*; what you may filter\non is the index **plus** the fields your account has configured.\n`rlm_filters` adds those deliberately, so that a field which exists in\nyour configuration but has never been given a value can still be filtered\non. `rlm_sorting` does not. A brand-new custom field with no data in it is\ntherefore filterable and not sortable, and asking to sort by it is\nsilently ignored rather than refused.\n\nSo do not derive one list from the other, and do not conclude a name is\ninvalid because sorting by it did nothing.\n\n**Multi-fields are those same names with a suffix.** `.ci` is the\ncase-insensitive form and `.keyword` the exact one; contacts and\ncompanies also carry `.word_start`. Sorting text is normally done on\n`.ci`, which is what RLM's own grids send:\n`rlm_sorting[taskType.ci][direction]=asc`. Sorting on the bare text form\nof a field gives an order that is rarely the one you want; use `.ci`, or\n`.keyword` when you need the exact form.\n\n**An unrecognised name is dropped, not rejected.** The object is filtered\ndown to permitted keys before anything reads it, so a misspelt field name\nreturns `200` in the endpoint's default order rather than an error, and a\nrequest whose keys are *all* misspelt sorts as though you had sent none.\nCheck the order you got back rather than assuming the key was honoured.\n",
        "schema": {
          "type": "object",
          "additionalProperties": {
            "type": "object",
            "properties": {
              "direction": {
                "type": "string",
                "enum": [
                  "asc",
                  "desc"
                ]
              },
              "priority": {
                "type": "integer"
              }
            }
          }
        }
      },
      "MatchFields": {
        "name": "match_fields",
        "in": "query",
        "required": false,
        "description": "Which fields `query` searches. Defaults to `name`. Accepts an array; it\nhas no effect unless `query` is also supplied.\n\n**The accepted names are contact search index fields**, the same set\n`rlm_sorting` draws on, so the same discovery path applies: every\nproperty of the contacts this endpoint returns is a valid name. Two are\npresent on every account - `name` and `company_names` - and the rest\ndepend on which fields the account has configured. A configured field is\naddressed through its `display` sub-key, as `<column_name>.display`;\nRLM's own contact search sends `first_name.display`, `last_name.display`\nand `email.display` alongside `name`, and those exist wherever the\naccount has those fields.\n\n**A name the index does not carry contributes no matches rather than an\nerror.** The failure mode is therefore an empty result set that looks\nlike \"no such contact\", not a `400` - so if a search returns nothing\nunexpectedly, check these names before concluding the data is absent.\nSending only unrecognised names searches nothing and matches nothing.\n",
        "schema": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "RlmFields": {
        "name": "rlm_fields",
        "in": "query",
        "required": false,
        "description": "Which columns to include in the exported spreadsheet, and in what order.\nDefaults to the standard column set for the record type.\n\n**This selects and orders; it cannot add.** The export already has a\ncolumn set - your organization's configured fields for the record type,\nplus the fixed columns that type's spreadsheet always carries. This\nparameter narrows that set to the names you list and reorders it to match\nthe order you list them in. A name that is not already a column of yours\ncontributes nothing; it does not create one.\n\n**The names are the response property names of the matching list\nendpoint** - `taskType`, `stage_name`, `created_at` - plus the column\nnames of your organization's custom fields. Read one page of the list\nendpoint to discover them.\n\n**Nothing validates this parameter, and no error tells you it was\nwrong.** An unrecognised name is ignored rather than refused; if none of\nthe names you send matches a column, the selection is empty rather than\nthe request failing. Omitting the parameter and sending it empty are the\nsame thing: both give the full default set.\n\nBecause the export is queued rather than rendered - the `200` says RLM\naccepted the job, not that a file exists - a mistake here surfaces in the\ndelivered spreadsheet, not in this response. Verify against the file.\n",
        "schema": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "JournalDescription": {
        "name": "description",
        "in": "query",
        "required": false,
        "description": "Free-text search across journal entry text. Note that this parameter is\ncalled `description` but the field it searches is `note` - there is no\n`description` key on a journal entry.\n",
        "schema": {
          "type": "string"
        }
      },
      "JournalSortBy": {
        "name": "sort_by",
        "in": "query",
        "required": false,
        "description": "A relevance hint. **It does not impose an order on the response**,\ndespite the name - treat it as advisory and do not build on it.\n\nJournal entries always come back the same way: pinned entries first, then\nby the search engine's relevance score, descending. `sort_by` does not\nreplace that. It is passed to the search engine as a boost on the named\nfield, which can only move an entry by changing its score - and how much\nthat moves anything, or whether it moves anything at all, depends on the\nscores already in play. A field name here is not a guarantee of ordering.\n\nThe value is one field name, not a list. A leading `-` weights the boost\ntwice as heavily; it does not mean descending.\n\n**If you need a specific order, sort `data` client-side.** No parameter\non this endpoint will do it for you: journals and attachments are the two\nv3 collections that do not accept `rlm_sorting`.\n\nThe web app's journal screens do sort by column, which is why this\nparameter reads as though it should. They reach the same finder by a\ndifferent route that applies a real database ordering. The API does not\nuse that route.\n",
        "schema": {
          "type": "string"
        }
      },
      "JournalCategoryId": {
        "name": "journal_category_id",
        "in": "query",
        "required": false,
        "description": "Restrict to one or more journal categories, by id.\n\n**No endpoint publishes the id list**, so read the ids off the entries\nthemselves: an entry that has a category carries `journal_category_id`\nbeside a human-readable `journal_category`. Page through this collection\nunfiltered once and collect the pairs you find. An uncategorised entry\ncarries neither, so skip those rather than treating them as a category.\n\nThat gives you the categories in *use*, not the full set - a category no\nentry has ever been given cannot be discovered this way, and there is no\nrequest that will list it. Cache what you harvest; the categories are\nshared reference data rather than per-account configuration, so the\nmapping does not vary between organizations and does not change often.\n\nAn id that matches nothing filters everything out and is not an error.\n",
        "schema": {
          "type": "array",
          "items": {
            "type": "integer"
          }
        }
      },
      "JournalCreatedById": {
        "name": "created_by_id",
        "in": "query",
        "required": false,
        "description": "Restrict to entries created by one or more users, by user id.\n\n**v3 has no user directory endpoint.** Discover ids the same way as for\n`journal_category_id`: an entry with a known author carries\n`created_by_id` beside that person's display name in `created_by`, and\nthe same pairing holds for `assigned_to_id` and `assigned_to`. Harvesting\nthose pairs is the only route from a person's name to their id.\n\nThe limits are worth stating plainly. You only learn about users who\nappear in entries you can already see, so this will not enumerate an\norganization's users, and a user who has written nothing is\nundiscoverable. Names are display names and are not unique - two people\nwith the same name are two ids, and nothing here disambiguates them.\n\nAn id belonging to another organization returns no entries rather than\nan error, so an empty result does not confirm an id is valid.\n",
        "schema": {
          "type": "array",
          "items": {
            "type": "integer"
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Authentication failed. The three headers must all be present, must all\nrefer to the same user, the key must be active, and v3 access must be\nenabled on **both** the organization and that user. Any one of these\nbeing false produces this response.\n"
      },
      "Forbidden": {
        "description": "Your credentials are valid but this user is not allowed to do this.\nTwo things produce it: asking for a company or contact endpoint on an\naccount without the Customer Management Module, and any per-record\npermission check the user fails.\n\nRetrying with the same credentials will not help - either the account\nneeds the module or the user needs the permission. Any endpoint can\nanswer this way.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ForbiddenError"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such record, or it is outside what the authenticating user can see.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/NotFoundError"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded - more than 2 requests per second. Wait the number of\nseconds given in `Retry-After` before retrying.\n",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying.",
            "schema": {
              "type": "integer"
            }
          }
        }
      },
      "ExportQueued": {
        "description": "The export was accepted and queued. The file is delivered to the\nrequesting user separately once generated.\n",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "queued": {
                  "type": "boolean",
                  "example": true
                }
              }
            }
          }
        }
      },
      "ExportFailed": {
        "description": "The export could not be queued - usually because the filter or sort\nparameters were not valid for this record type.\n",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "queued": {
                  "type": "boolean",
                  "example": false
                },
                "error": {
                  "type": "string",
                  "example": "There was an error with starting the export, please try again later."
                }
              }
            }
          }
        }
      },
      "LeadList": {
        "description": "Matching leads.",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ListEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Lead"
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "TaskList": {
        "description": "Matching tasks.",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ListEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Task"
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "CompanyList": {
        "description": "Matching companies.",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ListEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Company"
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "ContactList": {
        "description": "Matching contacts.",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ListEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Contact"
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "SampleList": {
        "description": "Matching samples.",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ListEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Sample"
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "JournalList": {
        "description": "Matching journal entries.",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ListEnvelope"
                },
                {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Journal"
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "AttachmentList": {
        "description": "The record's attachments. Not paginated, so `meta` and `links` are\nabsent.\n",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "data": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Attachment"
                  }
                },
                "edocs": {
                  "type": "array",
                  "description": "Electronic documents from a connected BMS system. Present\nonly when the record's organization has a BMS integration\nactive, and only here - the single-attachment endpoints never\ncarry it. Note that unlike `data`, these links cannot be\nfollowed with API credentials; see `Edoc`.\n",
                  "items": {
                    "$ref": "#/components/schemas/Edoc"
                  }
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "ListEnvelope": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
            }
          },
          "meta": {
            "$ref": "#/components/schemas/ListMeta"
          },
          "links": {
            "$ref": "#/components/schemas/ListLinks"
          }
        }
      },
      "ListMeta": {
        "type": "object",
        "properties": {
          "matches": {
            "type": "integer",
            "description": "Total records matching this query, across all pages.",
            "example": 42
          },
          "total": {
            "type": "integer",
            "description": "Total records of this type visible to the authenticating user,\nignoring the query. A denominator, not a result count.\n",
            "example": 1360
          }
        }
      },
      "ListLinks": {
        "type": "object",
        "description": "Pagination URLs. **Empty unless `page[number]` and `page[size]` were\nsupplied.** `first` and `prev` are absent on the first page; `next` and\n`last` are absent on the last.\n",
        "properties": {
          "self": {
            "type": "string"
          },
          "first": {
            "type": "string"
          },
          "prev": {
            "type": "string"
          },
          "next": {
            "type": "string"
          },
          "last": {
            "type": "string"
          }
        }
      },
      "NotFoundError": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "example": 404
          },
          "error": {
            "type": "string",
            "example": "Not Found"
          },
          "message": {
            "type": "string",
            "example": "Couldn't find Lead with 'id'=999999"
          }
        }
      },
      "ForbiddenError": {
        "type": "object",
        "description": "Same envelope as `NotFoundError`, with the reason in `message`.\n",
        "properties": {
          "status": {
            "type": "integer",
            "example": 403
          },
          "error": {
            "type": "string",
            "example": "Forbidden"
          },
          "message": {
            "type": "string",
            "example": "Your account does not have access to the Customer Management Module."
          }
        }
      },
      "CustomFieldValue": {
        "type": "object",
        "description": "The value of one custom field. Custom fields on leads, tasks,\ncompanies, contacts and samples all arrive in this shape: a `display`\nkey, plus one further key whose **name is the field's type** and whose\nvalue is the typed one.\n\nWhich extra key you get, by field type:\n\n- `currency`, `number`, `percentage` - a key of that name holding a\n  number.\n- `date`, `datetime` - `date`, an ISO 8601 timestamp shifted into the\n  organization's time zone. Date-only fields land at midday, not\n  midnight, so a time-zone conversion cannot push them onto the wrong\n  day.\n- `checkbox` - `checkbox`, a boolean.\n- `phone`, `phone_ext` - a key of that name holding the number in E.164\n  form. `display` holds the human-formatted version.\n- `system` - `system_id` and `system`, the id and name of the linked\n  record. Both are arrays when the field accepts several.\n- `user` - `user_id` and `user`, the id and name of the user.\n- `calculation` - one key per configured output type. A key is missing\n  entirely, rather than null, when that calculation could not be\n  evaluated.\n- everything else (`text`, `text_box`, `email`, `url`, `select`,\n  `multiple_select`, `state`, `address`, `time`, ...) - a key named\n  after the field type holding the raw value.\n\nEmpty strings are normalised to `null` throughout, so test for `null`\nrather than `\"\"`.\n\nThree of those types draw their value from a list rather than accepting\nfree text, and the list is not published here:\n\n- `select` - one value from that field's `field_options` on\n  `GET /api/v1/pipeline/{apiKey}`. Configured per pipeline, so there is\n  no fixed set; read it, do not hard-code it.\n- `multiple_select` - several values from the same `field_options`. The\n  `multiple_select` key holds them as an array; `display` holds the\n  comma-joined string. An empty selection normalises to `null` on both\n  keys rather than to `[]`.\n- `state` - one of the 60 fixed US state, territory and military codes.\n  Unlike the other two these never vary by pipeline; v1's\n  `LeadCreate.state` lists them.\n\nSee \"Which values a field can hold\" in the introduction.\n\nLocked fields, and fields whose name collides with a standard indexed\ncolumn, are the exception: those arrive as a bare scalar instead of an\nobject. See \"Custom fields, and why their values are objects\" in the\nintroduction.\n",
        "additionalProperties": true,
        "properties": {
          "display": {
            "type": "string",
            "nullable": true,
            "description": "The value formatted for display, exactly as the web app shows it -\ncurrency symbols, formatted dates, comma-joined multi-selects.\n`null` when the field has no value.\n"
          }
        }
      },
      "CustomFieldOrScalar": {
        "description": "What you find under a key that is not one of the documented properties.\nOn leads, tasks, companies, contacts and samples that means a custom\nfield, keyed by its `column_name`; on list responses it also covers the\nsearch index's `_type` and `sort`.\n\nA custom field is a `CustomFieldValue` object, unless it is locked or\nits name collides with a standard indexed column, in which case it is a\nbare scalar. Both forms are shown here because both really occur -\ndecide which you have by checking the type at runtime.\n",
        "anyOf": [
          {
            "$ref": "#/components/schemas/CustomFieldValue"
          },
          {
            "type": "string",
            "nullable": true
          },
          {
            "type": "number",
            "nullable": true
          },
          {
            "type": "boolean",
            "nullable": true
          },
          {
            "type": "array",
            "items": {
            }
          }
        ]
      },
      "Lead": {
        "type": "object",
        "description": "A lead.\n\n**Carries additional keys beyond those listed.** Every custom field\nconfigured on the lead's pipeline appears under its `column_name`, as a\n`CustomFieldValue` object (or a bare scalar if the field is locked or\ncollides with a standard column). The key set therefore differs between\npipelines - call `GET /api/v1/pipeline/{apiKey}` on the v1 API to\ndiscover which fields a given pipeline defines.\n\nList entries and single records carry the same keys. The only\ndifference is the search index's `_type`, which appears on list\nentries only; do not expect one form to be richer than the other.\n",
        "additionalProperties": {
          "$ref": "#/components/schemas/CustomFieldOrScalar"
        },
        "properties": {
          "id": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "Open",
              "Closed"
            ]
          },
          "name": {
            "type": "string"
          },
          "pipeline": {
            "type": "string"
          },
          "pipeline_id": {
            "type": "integer"
          },
          "pipeline_api_key": {
            "type": "string",
            "description": "The v1 pipeline API key for this lead's pipeline - the `{apiKey}`\npath segment on the v1 endpoints. Only present when the pipeline\nhas a key issued. Treat it as a credential: it is what a v1 client\nposts leads into this pipeline with.\n"
          },
          "organization_id": {
            "type": "integer"
          },
          "location_id": {
            "type": "integer",
            "nullable": true
          },
          "division_id": {
            "type": "integer",
            "nullable": true
          },
          "salesperson": {
            "type": "string",
            "nullable": true
          },
          "salesperson_id": {
            "type": "integer",
            "nullable": true
          },
          "stage_name": {
            "type": "string"
          },
          "stage_id": {
            "type": "integer",
            "nullable": true
          },
          "stage_class": {
            "type": "string",
            "nullable": true,
            "description": "The stage name as a URL-safe slug (`ready-to-order`), which the web\napp uses for styling. A lead that has not entered a stage yet\nreports `unassigned`, `assigned` or `accepted` instead. Derived\nfrom `stage_name` - filter on that, not on this.\n"
          },
          "status_color": {
            "type": "string",
            "nullable": true,
            "description": "The colour configured on the lead's stage, used for its badge in\nthe web app. One of a fixed palette of 21 names, which no endpoint\npublishes:\n\n`red` `pink` `purple` `deep-purple` `indigo` `blue` `light-blue`\n`cyan` `teal` `green` `light-green` `lime` `yellow` `amber`\n`orange` `deep-orange` `brown` `grey` `blue-grey` `black` `white`\n\nWhich stage gets which colour is configured per pipeline, so the\npalette is fixed but the mapping is not. Same substitutions as\n`stage_class` before the lead enters a stage - `unassigned`,\n`assigned` or `accepted` appear here in place of a colour.\n",
            "enum": [
              "red",
              "pink",
              "purple",
              "deep-purple",
              "indigo",
              "blue",
              "light-blue",
              "cyan",
              "teal",
              "green",
              "light-green",
              "lime",
              "yellow",
              "amber",
              "orange",
              "deep-orange",
              "brown",
              "grey",
              "blue-grey",
              "black",
              "white",
              "unassigned",
              "assigned",
              "accepted"
            ]
          },
          "company_id": {
            "type": "integer",
            "nullable": true
          },
          "contact_id": {
            "type": "integer",
            "nullable": true
          },
          "next_task_type": {
            "type": "string",
            "description": "Name of the task type on the lead's next upcoming task. Only\npresent when the lead has an upcoming task and that task has a type\nset.\n\nTask types are configured per account, so there is no fixed list.\nv3 has no endpoint for them; the only place the API publishes any is\n`pipeline_statuses[].task_types` on `GET /api/v1/pipeline/{apiKey}`,\nwhich gives the types selectable at each status.\n"
          },
          "next_task_name": {
            "type": "string",
            "description": "Subject of the lead's next upcoming task. Only present when the\nlead has one.\n"
          },
          "task_start_date": {
            "type": "string",
            "format": "date-time",
            "description": "When the next upcoming task starts. Only present when the lead has\none. All-day tasks are timestamped at midday in the organization's\ntime zone so that converting the value cannot move it a day.\n"
          },
          "next_task_start_time": {
            "type": "string",
            "description": "Start time of the next upcoming task, time of day only, formatted\nfor display (`02:30 PM`). Only present when the lead has an\nupcoming task; `null` when that task has no time on it.\n",
            "nullable": true
          },
          "next_task_end_time": {
            "type": "string",
            "description": "End time of the next upcoming task, in the same form as\n`next_task_start_time`.\n",
            "nullable": true
          },
          "next_task_overdue": {
            "type": "boolean",
            "description": "Whether the next upcoming task is past its end and still\nincomplete. Only present when the lead has an upcoming task. A task\nthat runs all day is not overdue on the day it is due.\n"
          },
          "samples_out": {
            "type": "string",
            "enum": [
              "Yes",
              "No"
            ],
            "description": "Whether the lead has samples currently with the customer. A\nformatted string, not a boolean.\n"
          },
          "open": {
            "type": "boolean"
          },
          "pending": {
            "type": "boolean"
          },
          "unassigned": {
            "type": "boolean"
          },
          "deleted": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "contacted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the customer was first contacted."
          },
          "closed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "sold_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "time_to_sale": {
            "type": "string",
            "nullable": true,
            "description": "How long the lead took to sell, worded for display\n(`2 days, 4 hours`). `null` until the lead is sold. Measured from\n`created_at` to `sold_at`.\n"
          },
          "time_to_sale_seconds": {
            "type": "number",
            "nullable": true,
            "description": "The same interval in seconds. Use this one for arithmetic.\n"
          },
          "time_to_contact": {
            "type": "string",
            "nullable": true,
            "description": "How long the lead took to reach first contact, worded the same way\nas `time_to_sale`. `null` until `contacted_at` is set.\n"
          },
          "time_to_contact_seconds": {
            "type": "number",
            "nullable": true,
            "description": "The same interval in seconds."
          },
          "created_by": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the user who created the lead."
          },
          "updated_by": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the user who last changed the lead."
          },
          "external_lead_source": {
            "type": "string",
            "nullable": true,
            "description": "Where a lead captured from outside RLM came from. Set by whatever\ncreated the lead; `null` on leads entered in the web app.\n"
          },
          "external_lead_id": {
            "type": "string",
            "nullable": true,
            "description": "The lead's identifier in that external system. Useful as the join\nkey when reconciling against the system that produced the lead.\n"
          },
          "external_lead_key": {
            "type": "string",
            "nullable": true,
            "description": "A second external identifier, set by the same source."
          }
        }
      },
      "Task": {
        "type": "object",
        "description": "A task: a piece of scheduled work hung off a lead, company or contact.\n\n**Carries additional keys beyond those listed.** A task on a lead also\ncarries that lead's pipeline fields - the ones its administrator marked\nfor the task listing, which is usually a subset of what the lead\nitself shows. They arrive as `CustomFieldValue` objects under their\n`column_name`, exactly as on `Lead`.\n\nThe task-specific keys are camelCase, unlike the rest of the API, and\nrelated keys are inconsistent about it - `task_start_date` sits next to\n`taskStartTime`. Copy the spellings from here rather than deriving\nthem.\n",
        "additionalProperties": {
          "$ref": "#/components/schemas/CustomFieldOrScalar"
        },
        "properties": {
          "id": {
            "type": "integer"
          },
          "leadId": {
            "type": "integer",
            "description": "The lead this task belongs to. Note the camelCase spelling, which\ndiffers from the rest of the API. **Prefer `lead_id`.**\n\nThese two keys are not interchangeable, and the difference is not\njust spelling:\n\n- `lead_id` is the stored reference. It is present whenever the task\n  has one.\n- `leadId` is read through the lead itself, so it is present only\n  when that lead is **still live**. A task whose lead has been\n  deleted keeps its `lead_id` and loses its `leadId`.\n\nSo `lead_id` present with `leadId` absent means \"this task points at\na deleted lead\" - which is also why `GET /api/v3/leads/{id}` may\nreturn a record for it. That combination is not rare; it is the\nnormal state of every task left behind by a deleted lead.\n\n⚠️ **Generated clients mishandle these two fields, in one of two\nways.** A code generator that converts key names to snake_case turns\n`leadId` into `lead_id` and collides with the real one. Some drop one\nof them: openapi-generator's Ruby output silently keeps whichever it\ndefines last. Others keep both and disambiguate by suffixing the\nsecond, which is the more dangerous outcome - two fields exist, so\nthe client looks correct, but the one *named* `lead_id` may be bound\nto the `leadId` key and the suffixed one to the field described here.\n\nSo do not just check that two fields survived. **Check which JSON key\neach generated field actually reads**, and assert it in a test on your\nside, before relying on the distinction above.\n"
          },
          "lead_id": {
            "type": "integer",
            "nullable": true,
            "description": "The lead this task belongs to, or `null` for a task on a company,\ncontact or user. Set even when that lead has been deleted - see\n`leadId` for how to tell the two cases apart.\n"
          },
          "taskRegarding": {
            "type": "string",
            "nullable": true,
            "description": "What the task is about - its subject line, and the text the web app\nshows in task lists. There is no `name` key on a task.\n"
          },
          "taskType": {
            "type": "string",
            "nullable": true,
            "description": "What kind of record the task hangs off. Not the same thing as\n`taskTypeName`.\n\n**It has a shape, so you can parse it rather than match it.** A task\nthat hangs off a lead reads `<lead word> - Primary` or\n`<lead word> - Secondary`; anything else is a bare word. So: split on\nthe first ` - `. The right-hand side, when there is one, is exactly\n`Primary` or `Secondary`. The left-hand side is the record type.\n\nFor tasks that do not hang off a lead, that record type is one of\nthree fixed values, and they are not themeable:\n\n`Contact`\n`Company`\n`User`\n\nFor tasks that do hang off a lead it is the organization's own word\nfor a lead, title-cased - `Lead` unless the account has renamed it,\nin which case it is whatever they renamed it to.\n\n**That word is the one part you cannot look up.** No v3 endpoint\npublishes an organization's lead wording, so there is no request that\ntells you what to expect before you see it. Read it off the value\ninstead: the tier suffix marks a lead task, and the text before it is\nthat account's lead word. Do not hard-code `Lead` - accounts that\nhave renamed it will not match, and this field is not an `enum`\nprecisely because the set is open.\n"
          },
          "taskTypeName": {
            "type": "string",
            "nullable": true,
            "description": "The task type chosen for this task (`Follow Up`, `Measure`), from\nthe organization's configured list. `null` when none was set.\n\nThat list is per account, so there is no fixed set to code against\nand v3 has no endpoint for it. The only place the API publishes any\ntask type names is `pipeline_statuses[].task_types` on\n`GET /api/v1/pipeline/{apiKey}` - the types selectable at each lead\nstatus. A task type used elsewhere in the account may not appear\nthere, so treat that list as a lower bound rather than the whole\nset.\n"
          },
          "taskAssignedTo": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the user the task is assigned to."
          },
          "taskStatus": {
            "type": "string",
            "enum": [
              "Completed",
              "Incomplete"
            ],
            "description": "The completed state as a display string. `completed` carries the\nsame thing as a boolean.\n"
          },
          "taskOverdue": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the task is past its end and still incomplete. A task that\nruns all day is not overdue on the day it is due.\n"
          },
          "task_start_date": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the task starts. A task with no start time is timestamped at\nmidday in the organization's time zone, not midnight, so that\nconverting the value cannot move it a day.\n"
          },
          "taskStartTime": {
            "type": "string",
            "nullable": true,
            "description": "Start time of day, formatted for display (`02:30 PM`). `null` on a\ntask with no time set.\n"
          },
          "task_end_date": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the task ends, with the same midday rule."
          },
          "taskEndTime": {
            "type": "string",
            "nullable": true,
            "description": "End time of day, formatted like `taskStartTime`."
          },
          "completed_date": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the task was completed. `null` while it is open."
          },
          "taskLocation": {
            "type": "string",
            "nullable": true,
            "description": "Name of the location the task belongs to - the lead's location, or\nthe assigned user's own location for a task with no lead.\n"
          },
          "stage_name": {
            "type": "string",
            "description": "The stage of the task's lead. Only present when the task is\nattached to a lead.\n"
          },
          "stage_class": {
            "type": "string",
            "nullable": true,
            "description": "The lead's stage as a slug, as on `Lead`. Only present when the\ntask is attached to a lead.\n"
          },
          "status_color": {
            "type": "string",
            "nullable": true,
            "description": "The colour of the lead's stage, as on `Lead`. Only present when the\ntask is attached to a lead.\n",
            "enum": [
              "red",
              "pink",
              "purple",
              "deep-purple",
              "indigo",
              "blue",
              "light-blue",
              "cyan",
              "teal",
              "green",
              "light-green",
              "lime",
              "yellow",
              "amber",
              "orange",
              "deep-orange",
              "brown",
              "grey",
              "blue-grey",
              "black",
              "white",
              "unassigned",
              "assigned",
              "accepted"
            ]
          },
          "pipeline": {
            "type": "string",
            "description": "Name of the lead's pipeline. Only present when the task is attached\nto a lead that has one.\n"
          },
          "organization_id": {
            "type": "integer"
          },
          "pipeline_id": {
            "type": "integer",
            "nullable": true,
            "description": "Taken from the task's lead. **Always present, and `null` on a task\nthat has no lead** - the same is true of `location_id`,\n`division_id`, `open` and `pending`.\n\nContrast `leadId`, `stage_name`, `stage_class`, `status_color` and\n`pipeline`, which are **omitted entirely** on a task without a lead\nrather than being null. If you are checking whether a task belongs\nto a lead, test for the presence of `leadId`, not the nullity of\nthis field.\n"
          },
          "location_id": {
            "type": "integer",
            "nullable": true,
            "description": "The lead's location. See `pipeline_id`."
          },
          "division_id": {
            "type": "integer",
            "nullable": true,
            "description": "The lead's division. See `pipeline_id`."
          },
          "company_id": {
            "type": "integer",
            "nullable": true
          },
          "contact_id": {
            "type": "integer",
            "nullable": true
          },
          "task_series_id": {
            "type": "integer",
            "nullable": true,
            "description": "Groups the occurrences of a recurring task. `null` on a one-off\ntask; shared by every task generated from the same series.\n"
          },
          "created_by_id": {
            "type": "integer",
            "nullable": true
          },
          "created_by": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the user who assigned the task. RLM treats \"created\nby\" and \"assigned by\" as the same person.\n"
          },
          "created_for_id": {
            "type": "integer",
            "nullable": true
          },
          "open": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the task's lead is open. See `pipeline_id` for when this is\nabsent.\n"
          },
          "pending": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the task's lead is still new - not yet assigned or\naccepted. See `pipeline_id` for when this is absent.\n"
          },
          "completed": {
            "type": "boolean"
          },
          "deleted": {
            "type": "boolean"
          },
          "visible": {
            "type": "boolean"
          },
          "private": {
            "type": "boolean",
            "description": "Whether the task is private to its owner. You only ever see a\nprivate task that the authenticating user is entitled to; others\nare filtered out of the results entirely rather than returned with\nthis flag set.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Company": {
        "type": "object",
        "description": "A company. Requires the Customer Management Module.\n\n**Carries additional keys beyond those listed.** The company fields\nconfigured for your organization appear under their `column_name` as\n`CustomFieldValue` objects, the same way pipeline fields do on `Lead`.\n",
        "additionalProperties": {
          "$ref": "#/components/schemas/CustomFieldOrScalar"
        },
        "properties": {
          "id": {
            "type": "integer"
          },
          "company_id": {
            "type": "string",
            "description": "The same id again, as a string. It exists so that company records\ncan be told apart from other record types in a merged list. Match\non `id`.\n"
          },
          "name": {
            "type": "string"
          },
          "name_lower": {
            "type": "string",
            "nullable": true,
            "description": "`name` lower-cased. The search index sorts on it, and it is the\ndefault order of `GET /api/v3/companies`.\n"
          },
          "pipelines": {
            "type": "array",
            "description": "Names of the pipelines this company's leads sit in. Only present\nwhen the company has at least one lead.\n",
            "items": {
              "type": "string"
            }
          },
          "contacts_count": {
            "type": "integer",
            "description": "How many contacts are attached to the company. On the\nsingle-record endpoint this is counted live; on the list endpoints\nit comes from the search index and can lag behind - a company with\none contact has been seen reporting `0` in a list and `1` on\n`show`. Do not use the list value to decide whether to fetch the\ncontacts.\n"
          },
          "organization_id": {
            "type": "integer"
          },
          "deleted": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the user who created the company."
          },
          "updated_by": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the user who last changed it."
          }
        }
      },
      "Contact": {
        "type": "object",
        "description": "A contact. Requires the Customer Management Module.\n\n**Carries additional keys beyond those listed.** The contact fields\nconfigured for your organization appear under their `column_name` as\n`CustomFieldValue` objects, the same way pipeline fields do on `Lead`.\n\n**There is no top-level `first_name` or `last_name` string.** Use\n`name`. Organizations that have first and last name provisioned as\ncontact fields do get those keys, but as `CustomFieldValue` objects -\n`{\"display\": \"Con\", \"text\": \"Con\"}` - not as the plain strings you\nmight expect, and organizations without them get nothing at all. The\nlower-cased sort keys below are the only reliable place the two halves\nappear separately.\n",
        "additionalProperties": {
          "$ref": "#/components/schemas/CustomFieldOrScalar"
        },
        "properties": {
          "id": {
            "type": "integer"
          },
          "contact_id": {
            "type": "string",
            "description": "The same id again, as a string, mirroring `Company.company_id`.\nMatch on `id`.\n"
          },
          "name": {
            "type": "string",
            "nullable": true,
            "description": "The contact's full display name, assembled by RLM. This is the\nname key to read.\n"
          },
          "first_name_lower": {
            "type": "string",
            "nullable": true,
            "description": "The contact's first name, lower-cased. Present for sorting rather\nthan display, but it is a plain string and always there.\n"
          },
          "last_name_lower": {
            "type": "string",
            "nullable": true,
            "description": "The contact's last name, lower-cased. The default order of\n`GET /api/v3/contacts` is this then `first_name_lower`.\n"
          },
          "company_names": {
            "type": "array",
            "description": "Names of the companies the contact belongs to. Only present when\nthe contact belongs to at least one.\n",
            "items": {
              "type": "string"
            }
          },
          "pipelines": {
            "type": "array",
            "description": "Names of the pipelines this contact's leads sit in. Only present\nwhen the contact has at least one lead.\n",
            "items": {
              "type": "string"
            }
          },
          "organization_id": {
            "type": "integer"
          },
          "company_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "deleted": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the user who created the contact."
          },
          "updated_by": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the user who last changed it."
          }
        }
      },
      "Sample": {
        "type": "object",
        "description": "A physical product sample loaned out during a sale.\n\n**Carries additional keys beyond those listed.** A sample also carries\nthe pipeline fields of the lead it belongs to - the ones its\nadministrator marked for the sample listing - as `CustomFieldValue`\nobjects under their `column_name`.\n\nMost of the sample-specific keys are camelCase; the rest of the record\nis snake_case. Copy the spellings here rather than deriving them. There\nis no `created_at` on a sample, only `updated_at`.\n",
        "additionalProperties": {
          "$ref": "#/components/schemas/CustomFieldOrScalar"
        },
        "properties": {
          "id": {
            "type": "integer"
          },
          "organization_id": {
            "type": "integer"
          },
          "leadId": {
            "type": "integer",
            "nullable": true,
            "description": "The associated lead. Note the camelCase spelling of this key, which\ndiffers from the rest of the API.\n"
          },
          "samplePipelineName": {
            "type": "string",
            "description": "Name of the pipeline the sample belongs to. Only present when the\nsample is on a pipeline.\n"
          },
          "sampleLocationName": {
            "type": "string",
            "description": "Name of the lead's location. Only present when the sample is\nattached to a lead.\n"
          },
          "sampleDivisionName": {
            "type": "string",
            "description": "Name of the lead's division. Only present when the sample is\nattached to a lead.\n"
          },
          "sampleSalesperson": {
            "type": "string",
            "description": "Display name of the lead's salesperson. Only present when the\nsample is attached to a lead that has one assigned.\n"
          },
          "stage_name": {
            "type": "string",
            "description": "The stage of the sample's lead. Only present when the sample is\nattached to a lead.\n"
          },
          "stage_class": {
            "type": "string",
            "nullable": true,
            "description": "The lead's stage as a slug, as on `Lead`. Only present when the\nsample is attached to a lead.\n"
          },
          "status_color": {
            "type": "string",
            "nullable": true,
            "description": "The colour of the lead's stage, as on `Lead`. Only present when the\nsample is attached to a lead.\n",
            "enum": [
              "red",
              "pink",
              "purple",
              "deep-purple",
              "indigo",
              "blue",
              "light-blue",
              "cyan",
              "teal",
              "green",
              "light-green",
              "lime",
              "yellow",
              "amber",
              "orange",
              "deep-orange",
              "brown",
              "grey",
              "blue-grey",
              "black",
              "white",
              "unassigned",
              "assigned",
              "accepted"
            ]
          },
          "sampleBrandName": {
            "type": "string",
            "nullable": true,
            "description": "Brand of the sampled product."
          },
          "sampleItemType": {
            "type": "string",
            "nullable": true,
            "description": "The product's item name - what kind of thing it is."
          },
          "sampleItemNumber": {
            "type": "string",
            "nullable": true,
            "description": "The supplier's item or SKU number."
          },
          "sampleStyleName": {
            "type": "string",
            "nullable": true,
            "description": "Style of the sampled product."
          },
          "sampleColorName": {
            "type": "string",
            "nullable": true,
            "description": "Colour of the sampled product."
          },
          "status": {
            "type": "string",
            "enum": [
              "no-return",
              "on-order",
              "with-customer",
              "returned"
            ],
            "description": "The raw status, and the value `rlm_filters[status]` takes.\n\n⚠️ **`no-return` is a real status that you cannot filter for.** A\nsample can carry it, but the `/list/{filter}` paths accept only\n`with-customer`, `on-order`, `returned` and `all` - anything else,\nincluding `no-return`, silently falls back to `all`. To isolate\n`no-return` samples, request `all` and filter client-side, or use\n`rlm_filters[status]`.\n"
          },
          "sampleStatus": {
            "type": "string",
            "enum": [
              "No Return",
              "On Order",
              "With Customer",
              "Returned"
            ],
            "description": "The same status title-cased for display, one of exactly four values\n- `With Customer` where `status` reads `with-customer`. Derived from\n`status`, so filter and compare on that one, not on this.\n"
          },
          "status_date": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "The date the current status is measured against. What it means\ndepends on `status`: the estimated delivery date while `on-order`,\nthe approximate return date while `with-customer`, and the date it\ncame back once `returned`. Timestamped at midday in the\norganization's time zone rather than midnight, so converting it\ncannot move it a day.\n"
          },
          "sampleStatusDateOverdue": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether `status_date` has passed - for a sample still with the\ncustomer, that it is overdue for return.\n"
          },
          "sampleQuoteAmount": {
            "type": "number",
            "nullable": true,
            "description": "The price quoted for the sampled product."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Journal": {
        "type": "object",
        "description": "One entry in a record's activity history.\n\n**The entry text is `note`. There is no `description` field on a\njournal entry** - `description` is a query *parameter* on these\nendpoints, which searches the note text, and the two are easy to\nconfuse. Read `note`.\n\n**The four resolved display names are on list responses only.**\n`journal_category`, `created_by`, `assigned_to` and `task_type` are\nlooked up and attached while building a list; a single entry fetched\nby id carries only the matching `*_id` keys. If you need the names for\none entry, either read it out of the list response or resolve the ids\nyourself.\n",
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "integer"
          },
          "note": {
            "type": "string",
            "nullable": true,
            "description": "The entry text. Long notes are truncated on save, so this is what\nRLM stored rather than necessarily what was typed.\n"
          },
          "journalable_id": {
            "type": "integer",
            "description": "Id of the record the entry is attached to."
          },
          "journalable_type": {
            "type": "string",
            "x-recognised-values": [
              "Company",
              "Contact",
              "Lead",
              "Task",
              "TaskSeries",
              "User"
            ],
            "description": "Type of the record the entry is attached to. With `journalable_id`\nthis is the entry's owner. Six values:\n\n`Company`\n`Contact`\n`Lead`\n`Task`\n`TaskSeries`\n`User`\n\n`TaskSeries` is the one most likely to break a client that switches\non this field. It is the recurring-task template that a repeating\ntask was generated from, and **v3 has no route that reaches one** -\nno `/api/v3/task_series/...` path exists, and it is not a parent\nsegment anywhere. So `journalable_id` is not fetchable when the type\nis `TaskSeries`. Treat this as a type you can display but not\nfollow, and make that the default for any value you do not know\nrather than a special case for this one.\n\nNot an `enum`: RLM has carried journal rows pointing at models that\nno longer exist, so a value outside this list is possible in old\ndata. An `enum` would make a generated client raise on one.\n"
          },
          "tags": {
            "type": "array",
            "description": "Labels the search index derives from the entry, used for filtering\nin the web app. Each is either `Type/id` - `Lead/123`, `Stage/45`,\n`LeadStatus/7`, `Company/9`, `Contact/12`, plus the entry's own\n`journalable_type`/`journalable_id` - or the bare string `Pinned`\non a pinned entry. Derived, so treat them as a convenience rather\nthan something to key on.\n",
            "items": {
              "type": "string"
            }
          },
          "journal_category_id": {
            "type": "integer",
            "nullable": true
          },
          "journal_category": {
            "type": "string",
            "nullable": true,
            "description": "Category name. List responses only - see the note above.\n"
          },
          "created_by_id": {
            "type": "integer",
            "nullable": true
          },
          "created_by": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the author. List responses only.\n"
          },
          "assigned_to_id": {
            "type": "integer",
            "nullable": true
          },
          "assigned_to": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the user the underlying record is assigned to.\nList responses only.\n"
          },
          "task_type_id": {
            "type": "integer",
            "nullable": true
          },
          "task_type": {
            "type": "string",
            "nullable": true,
            "description": "Task type name. List responses only.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the entry was soft-deleted; `null` on a live entry. There is\nno `updated_at` on a journal entry.\n"
          }
        }
      },
      "Attachment": {
        "type": "object",
        "description": "Metadata for a file attached to a record. The response describes and\nlinks to the file rather than embedding its contents. These six keys\nare the whole payload; there is no `updated_at`.\n",
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "integer"
          },
          "file_name": {
            "type": "string",
            "nullable": true,
            "description": "The uploaded file's own name, including its extension. Use this for\nthe file type; `attachment_name` will not tell you.\n"
          },
          "attachment_name": {
            "type": "string",
            "nullable": true,
            "description": "The label a user gave the attachment in RLM. Free text, and often\nunrelated to `file_name`.\n"
          },
          "link": {
            "type": "string",
            "format": "uri",
            "description": "Where to download the file. In production this is an absolute URL\nto the file storage service rather than to RLM, so fetching it is\nan ordinary HTTP GET that does not use your v3 headers. Nothing in\nthis API governs who may follow the link or for how long - that is\na property of the storage URL, not of your credentials.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "string",
            "description": "Display name of the user who uploaded the file. Only present when\nthe attachment records an uploader.\n"
          }
        }
      },
      "Edoc": {
        "type": "object",
        "description": "An electronic document held in a connected BMS system, listed alongside\nRLM's own attachments. The same key names as `Attachment`, but the id\nand the link mean different things.\n",
        "properties": {
          "id": {
            "type": "integer",
            "description": "The document's id in the BMS, not an RLM id. It does not resolve\nagainst any endpoint in this API.\n"
          },
          "file_name": {
            "type": "string",
            "nullable": true
          },
          "attachment_name": {
            "type": "string",
            "nullable": true,
            "description": "The BMS description of the document."
          },
          "link": {
            "type": "string",
            "format": "uri",
            "description": "An RLM URL that proxies the document out of the BMS. **It is\nauthenticated by a web-app session, not by the v3 headers**, so an\nAPI-only client cannot follow it - sending the three v3 headers to\nthis URL will not get you the file. It is usable by a signed-in\nperson in a browser, and that is all.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "string",
            "nullable": true,
            "description": "Where in the BMS the document came from, rather than a user - for\nexample `BMS Quote 12345`.\n"
          }
        }
      }
    }
  }
}