{
  "info": {
    "name": "Open HelpDesk API",
    "description": "Generated from the OpenAPI document this API serves. Set `token` to an API key from Settings → API & webhooks, and `baseUrl` to your workspace.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "version": {
      "major": 1,
      "minor": 0,
      "patch": 0
    }
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{token}}",
        "type": "string"
      }
    ]
  },
  "item": [
    {
      "name": "Tickets",
      "description": "Requests, their conversations and their files.",
      "item": [
        {
          "name": "List tickets",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/tickets",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "tickets"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "",
                  "description": "Comma-separated statuses.",
                  "disabled": true
                },
                {
                  "key": "priority",
                  "value": "",
                  "description": "Comma-separated priorities.",
                  "disabled": true
                },
                {
                  "key": "assignee_id",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "organization_id",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "requester_id",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "tag",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "updated_since",
                  "value": "",
                  "description": "Only tickets touched since this instant — the basis of an incremental sync.",
                  "disabled": true
                }
              ]
            },
            "description": "List tickets"
          },
          "response": []
        },
        {
          "name": "Create a ticket",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/tickets",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "tickets"
              ]
            },
            "description": "Goes through the same path as an inbound email: the requester is found or created, rules and SLA policies run, and outbound webhooks fire.\n\nCreate a ticket",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requester_email\": \"julien.lambert@nordfil.fr\",\n  \"subject\": \"Cannot export invoices as PDF\",\n  \"message\": \"Hello, the PDF export has been failing since this morning.\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Read a ticket",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/tickets/:number",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "tickets",
                ":number"
              ],
              "variable": [
                {
                  "key": "number",
                  "value": "",
                  "description": "The number in the path."
                }
              ]
            },
            "description": "Read a ticket"
          },
          "response": []
        },
        {
          "name": "Update a ticket",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/tickets/:number",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "tickets",
                ":number"
              ],
              "variable": [
                {
                  "key": "number",
                  "value": "",
                  "description": "The number in the path."
                }
              ]
            },
            "description": "`custom_fields` is merged, not replaced: omitting a key leaves it alone.\n\nUpdate a ticket",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"status\": \"new\",\n  \"priority\": \"low\",\n  \"assignee_id\": \"3f2a1c94-8e5b-4d17-9f60-2c7b1a0d5e83\",\n  \"organization_id\": \"3f2a1c94-8e5b-4d17-9f60-2c7b1a0d5e83\",\n  \"subject\": \"\",\n  \"type\": \"\",\n  \"tags\": [\n    \"\"\n  ],\n  \"custom_fields\": {}\n}"
            }
          },
          "response": []
        },
        {
          "name": "List a ticket's messages",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/tickets/:number/messages",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "tickets",
                ":number",
                "messages"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                }
              ],
              "variable": [
                {
                  "key": "number",
                  "value": "",
                  "description": "The number in the path."
                }
              ]
            },
            "description": "Oldest first — the order a human reads the thread in.\n\nList a ticket's messages"
          },
          "response": []
        },
        {
          "name": "Add a reply or an internal note",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/tickets/:number/messages",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "tickets",
                ":number",
                "messages"
              ],
              "variable": [
                {
                  "key": "number",
                  "value": "",
                  "description": "The number in the path."
                }
              ]
            },
            "description": "A public reply reaches the customer and fires the same rules the product does; an internal note stays inside the workspace.\n\nAdd a reply or an internal note",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"body\": \"We have shipped a fix — could you try again?\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "List every file on a ticket",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/tickets/:number/attachments",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "tickets",
                ":number",
                "attachments"
              ],
              "variable": [
                {
                  "key": "number",
                  "value": "",
                  "description": "The number in the path."
                }
              ]
            },
            "description": "List every file on a ticket"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Contacts",
      "description": "The people who write in.",
      "item": [
        {
          "name": "List contacts",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/contacts",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "contacts"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                },
                {
                  "key": "email",
                  "value": "",
                  "description": "Exact address.",
                  "disabled": true
                }
              ]
            },
            "description": "List contacts"
          },
          "response": []
        },
        {
          "name": "Create a contact",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/contacts",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "contacts"
              ]
            },
            "description": "Idempotent by email: an address that already exists comes back with 200, not a duplicate.\n\nCreate a contact",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"julien.lambert@nordfil.fr\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Read a contact",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/contacts/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "contacts",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "The id in the path."
                }
              ]
            },
            "description": "Read a contact"
          },
          "response": []
        },
        {
          "name": "Update a contact",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/contacts/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "contacts",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "The id in the path."
                }
              ]
            },
            "description": "Update a contact",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"someone@example.com\",\n  \"name\": \"\",\n  \"phone\": \"\",\n  \"locale\": \"\",\n  \"blocked\": false,\n  \"custom_fields\": {}\n}"
            }
          },
          "response": []
        },
        {
          "name": "Delete a contact (GDPR erasure)",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/contacts/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "contacts",
                ":id"
              ],
              "query": [
                {
                  "key": "delete_tickets",
                  "value": "",
                  "description": "Also erase their tickets.",
                  "disabled": true
                }
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "The id in the path."
                }
              ]
            },
            "description": "Refused with 409 while the person still has tickets, unless `delete_tickets=true` is passed — erasing a support history should be asked for, not inferred. To merely stop someone writing in, set `blocked` instead.\n\nDelete a contact (GDPR erasure)"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Organizations",
      "description": "The companies they belong to.",
      "item": [
        {
          "name": "List organizations",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/organizations",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "organizations"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "List organizations"
          },
          "response": []
        },
        {
          "name": "Create an organization",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/organizations",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "organizations"
              ]
            },
            "description": "Create an organization",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Nordfil SAS\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Read an organization",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/organizations/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "organizations",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "The id in the path."
                }
              ]
            },
            "description": "Read an organization"
          },
          "response": []
        },
        {
          "name": "Update an organization",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/organizations/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "organizations",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "The id in the path."
                }
              ]
            },
            "description": "Update an organization",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"\",\n  \"email_domains\": [\n    \"\"\n  ],\n  \"shared_tickets\": false,\n  \"notes\": \"\",\n  \"custom_fields\": {}\n}"
            }
          },
          "response": []
        },
        {
          "name": "Delete an organization",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/organizations/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "organizations",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "The id in the path."
                }
              ]
            },
            "description": "Its tickets are detached, never deleted: a customer's history outlives the company record.\n\nDelete an organization"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Knowledge base",
      "description": "Help centre categories and articles.",
      "item": [
        {
          "name": "List categories",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/kb/categories",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "kb",
                "categories"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "List categories"
          },
          "response": []
        },
        {
          "name": "List articles",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/kb/articles",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "kb",
                "articles"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "category_id",
                  "value": "",
                  "disabled": true
                }
              ]
            },
            "description": "List articles"
          },
          "response": []
        },
        {
          "name": "Create an article",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/kb/articles",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "kb",
                "articles"
              ]
            },
            "description": "Lands as a draft unless `status` says otherwise: publishing puts text in front of customers.\n\nCreate an article",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"title\": \"Exporting your invoices\",\n  \"category_id\": \"3f2a1c94-8e5b-4d17-9f60-2c7b1a0d5e83\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Read an article",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/kb/articles/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "kb",
                "articles",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "The id in the path."
                }
              ]
            },
            "description": "Read an article"
          },
          "response": []
        },
        {
          "name": "Update an article",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/kb/articles/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "kb",
                "articles",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "The id in the path."
                }
              ]
            },
            "description": "`published_at` is stamped on the first publication only — a re-publish never rewrites it.\n\nUpdate an article",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"title\": \"\",\n  \"body_html\": \"\",\n  \"category_id\": \"3f2a1c94-8e5b-4d17-9f60-2c7b1a0d5e83\",\n  \"slug\": \"\",\n  \"status\": \"draft\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Delete an article",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/kb/articles/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "kb",
                "articles",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "The id in the path."
                }
              ]
            },
            "description": "Delete an article"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Attachments",
      "description": "Files exchanged on tickets.",
      "item": [
        {
          "name": "Download a file",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/attachments/:id/download",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "attachments",
                ":id",
                "download"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "The id in the path."
                }
              ]
            },
            "description": "Streams the file itself. The API key is the only way in — there is no signed URL.\n\nDownload a file"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Workspace",
      "description": "Agents, teams and the configuration rules run on.",
      "item": [
        {
          "name": "List agents",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/agents",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "agents"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "List agents"
          },
          "response": []
        },
        {
          "name": "List teams",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/teams",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "teams"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "List teams"
          },
          "response": []
        },
        {
          "name": "List macros",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/macros",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "macros"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "List macros"
          },
          "response": []
        },
        {
          "name": "List SLA policies",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/sla-policies",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "sla-policies"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "List SLA policies"
          },
          "response": []
        },
        {
          "name": "List saved views",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/views",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "views"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "List saved views"
          },
          "response": []
        },
        {
          "name": "List custom ticket fields",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/ticket-fields",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "ticket-fields"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "List custom ticket fields"
          },
          "response": []
        },
        {
          "name": "List tags in use",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/tags",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "tags"
              ]
            },
            "description": "Derived from the tickets themselves — the product keeps no separate tag registry.\n\nList tags in use"
          },
          "response": []
        },
        {
          "name": "List satisfaction responses",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/csat",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "csat"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "",
                  "description": "Rows per page.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "The `next_cursor` of the previous page.",
                  "disabled": true
                }
              ]
            },
            "description": "List satisfaction responses"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Mobile",
      "description": "Signing in on a phone, and what only a signed-in phone can ask: who am I, notify me, search.",
      "item": [
        {
          "name": "Mark a ticket read",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/tickets/:number/read",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "tickets",
                ":number",
                "read"
              ],
              "variable": [
                {
                  "key": "number",
                  "value": "",
                  "description": "The number in the path."
                }
              ]
            },
            "description": "Clears the unread dot for the calling agent, and for them alone — a colleague\nopening the ticket does not clear mine.\n\nExplicit rather than implied by `GET /tickets/{number}`: a read that happens as a\nside effect of fetching cannot be retried, prefetched or cached, and a client\nfetches a ticket for reasons other than a human reading it.\n\nMark a ticket read"
          },
          "response": []
        },
        {
          "name": "Sign in on a device",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/login",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "login"
              ]
            },
            "description": "Exchanges an agent's credentials for a token bound to this phone, revocable on\nits own. Call it on the workspace's own address (`{slug}.$BASE_DOMAIN`): the\nworkspace comes from the host, never from the body.\n\nUnauthenticated, so it is limited separately — ten attempts per address and forty\nper source every five minutes.\n\nSign in on a device",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"sarah@acme.fr\",\n  \"password\": \"\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Start an SSO sign-in from the app",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/auth/authorize",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "authorize"
              ],
              "query": [
                {
                  "key": "code_challenge",
                  "value": "",
                  "description": "base64url(SHA-256(verifier)).",
                  "disabled": true
                },
                {
                  "key": "state",
                  "value": "",
                  "description": "Echoed back untouched.",
                  "disabled": true
                }
              ]
            },
            "description": "The browser leg of a single sign-on. Open the workspace's login page in a system\nbrowser with `?next=/api/v1/auth/authorize?code_challenge=…`; once the identity\nprovider has signed the agent in, this route redirects to the app's URL scheme\ncarrying a one-time code, which the app spends on `/auth/exchange`.\n\nThe destination is the scheme configured on the instance — there is no\n`redirect_uri` parameter, by design. PKCE S256 is required: a custom scheme is not\nexclusive to one installed app.\n\nStart an SSO sign-in from the app"
          },
          "response": []
        },
        {
          "name": "Finish an SSO sign-in",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auth/exchange",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "exchange"
              ]
            },
            "description": "Trades the one-time code for a device session. Single use, two minutes, and worthless without the verifier the app kept.\n\nFinish an SSO sign-in",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"code\": \"\",\n  \"code_verifier\": \"\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Sign this device out",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/auth/logout",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auth",
                "logout"
              ]
            },
            "description": "Revokes the session the call is made with, and its push registrations. Other devices and browser sessions are untouched.\n\nSign this device out"
          },
          "response": []
        },
        {
          "name": "The agent this device is signed in as",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/me",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me"
              ]
            },
            "description": "Identity, role and teams — what \"my tickets\" and the view switcher are built from. Needs an agent session: a workspace API key has nobody behind it and gets 403 `agent_required`.\n\nThe agent this device is signed in as"
          },
          "response": []
        },
        {
          "name": "Take work, or stop taking work",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/me",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "me"
              ]
            },
            "description": "`available` is the only writable field, and it is not cosmetic: round-robin\nassignment only ever picks an available agent, so turning it off on the way into a\nmeeting is how a queue stops filling up for somebody who cannot answer.\n\nA name, an email or a role are the workspace's business. An endpoint called `/me`\nthat could change a role would be a privilege escalation with a friendly name.\n\nTake work, or stop taking work",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"available\": false\n}"
            }
          },
          "response": []
        },
        {
          "name": "Register for push notifications",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/devices",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices"
              ]
            },
            "description": "Upsert on the token: the operating system rotates and reissues it, so re-registering updates the row instead of leaving a trail of them.\n\nRegister for push notifications",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"push_token\": \"\",\n  \"platform\": \"ios\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Stop notifying this device",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/devices/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "The id in the path."
                }
              ]
            },
            "description": "An agent revokes their own registrations; a device is not workspace furniture.\n\nStop notifying this device"
          },
          "response": []
        },
        {
          "name": "What happened on my tickets while I was away",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/notifications",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "notifications"
              ]
            },
            "description": "The same feed the web topbar shows, as data rather than as sentences — a phone\nwrites its own wording, in the language of the phone.\n\n`read` is not per item and cannot be: the feed is derived from tickets and messages,\nso there is no row to mark. It is a waterline (see POST /notifications/read).\n\nWhat happened on my tickets while I was away"
          },
          "response": []
        },
        {
          "name": "Mark everything read",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/notifications/read",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "notifications",
                "read"
              ]
            },
            "description": "Moves the waterline to now. Shared with the web topbar's own button, so clearing the badge on a phone clears it in the browser too.\n\nMark everything read"
          },
          "response": []
        },
        {
          "name": "Search tickets, contacts, organizations and articles",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/search",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "search"
              ],
              "query": [
                {
                  "key": "q",
                  "value": "",
                  "description": "A ticket number, a subject, a name, an address. Under two characters, every list comes back empty.",
                  "disabled": true
                }
              ]
            },
            "description": "One query across four collections, as the search box offers it. A few top matches per kind, no pagination — page `/tickets` with real filters to walk a set.\n\nSearch tickets, contacts, organizations and articles"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Customer app",
      "description": "What a customer may do with their own requests. A namespace of its own, not the agent routes with a narrower credential.",
      "item": [
        {
          "name": "Email a customer their sign-in link",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/portal/auth/request-link",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "auth",
                "request-link"
              ]
            },
            "description": "Customers have no password — the portal signs them in by emailed link, and the app\nuses the same one. The link lands in a browser, which hands the session to the app\nthrough `/portal/auth/handoff`, so generate a PKCE verifier first and pass its\nchallenge here: it is what the handover code will be bound to.\n\nAlways 202. An address with no account, a blocked one and a real one are\nindistinguishable in the answer — anything else would turn this into a directory of\na workspace's customers.\n\nEmail a customer their sign-in link",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"someone@example.com\",\n  \"code_challenge\": \"\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Hand an emailed sign-in over to the app",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/portal/auth/handoff",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "auth",
                "handoff"
              ],
              "query": [
                {
                  "key": "code_challenge",
                  "value": "",
                  "description": "The challenge the link was built with.",
                  "disabled": true
                },
                {
                  "key": "state",
                  "value": "",
                  "disabled": true
                }
              ]
            },
            "description": "Where the magic link ends up. With the portal cookie the browser just received, it redirects to the app's URL scheme carrying a one-time code. Not called by the app itself — the app waits for the deep link.\n\nHand an emailed sign-in over to the app"
          },
          "response": []
        },
        {
          "name": "Finish a customer sign-in",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/portal/auth/exchange",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "auth",
                "exchange"
              ]
            },
            "description": "Trades the handover code for a session bound to this phone. Single use, two minutes.\n\nFinish a customer sign-in",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"code\": \"\",\n  \"code_verifier\": \"\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Sign this device out",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/portal/auth/logout",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "auth",
                "logout"
              ]
            },
            "description": "Sign this device out"
          },
          "response": []
        },
        {
          "name": "The customer this device is signed in as",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/portal/me",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "me"
              ]
            },
            "description": "Identity and organization (MC-05). `workspace.locale` is the language to render in: the product runs one language per workspace, so there is deliberately no per-customer language to write.\n\nThe customer this device is signed in as"
          },
          "response": []
        },
        {
          "name": "The requests a customer may see",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/portal/requests",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "requests"
              ],
              "query": [
                {
                  "key": "scope",
                  "value": "",
                  "disabled": true
                }
              ]
            },
            "description": "`scope=mine` is theirs; `scope=organization` is their company's, and only where that company has ticket sharing turned on. The fifty most recently updated, unpaginated — `next_cursor` is always null.\n\nThe requests a customer may see"
          },
          "response": []
        },
        {
          "name": "Submit a request",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/portal/requests",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "requests"
              ]
            },
            "description": "Files the request on the `portal` channel and runs the same rules, SLA policies and notifications as the web portal — a request typed on a phone is not a second kind of request.\n\nSubmit a request",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"subject\": \"\",\n  \"body\": \"\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Read a request and its conversation",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/portal/requests/:number",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "requests",
                ":number"
              ],
              "variable": [
                {
                  "key": "number",
                  "value": "",
                  "description": "The number in the path."
                }
              ]
            },
            "description": "Public replies only — internal notes are excluded by the query itself. A request that is not the customer's answers 404, never 403: otherwise the numbers become a way to count a workspace's tickets.\n\nRead a request and its conversation"
          },
          "response": []
        },
        {
          "name": "Answer on your own request",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/portal/requests/:number/messages",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "requests",
                ":number",
                "messages"
              ],
              "variable": [
                {
                  "key": "number",
                  "value": "",
                  "description": "The number in the path."
                }
              ]
            },
            "description": "Only the requester may write; a colleague who can read the company's requests cannot answer in their thread. A reply reopens what was waiting, on hold or resolved.\n\nAnswer on your own request",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"body\": \"\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Register for push notifications",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/portal/devices",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "devices"
              ]
            },
            "description": "Upsert on the token, like the agents' registrations.\n\nRegister for push notifications",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"push_token\": \"\",\n  \"platform\": \"ios\"\n}"
            }
          },
          "response": []
        },
        {
          "name": "Stop notifying this device",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/portal/devices/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "devices",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "The id in the path."
                }
              ]
            },
            "description": "Stop notifying this device"
          },
          "response": []
        },
        {
          "name": "News about my requests",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/portal/notifications",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "notifications"
              ]
            },
            "description": "Somebody answered, or a request was resolved (MC-04). Nothing about a colleague's request: belonging to an organization that shares its tickets lets someone read them, which is not being notified about them.\n\nNews about my requests"
          },
          "response": []
        },
        {
          "name": "Mark everything read",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/portal/notifications/read",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portal",
                "notifications",
                "read"
              ]
            },
            "description": "Mark everything read"
          },
          "response": []
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://{{workspace}}.open-helpdesk.com/api/v1",
      "description": "Your workspace's API root."
    },
    {
      "key": "workspace",
      "value": "acme",
      "description": "The subdomain of your workspace."
    },
    {
      "key": "token",
      "value": "",
      "type": "string",
      "description": "An API key: Settings → API & webhooks. Keep it out of source control."
    }
  ]
}
