{"openapi":"3.1.0","info":{"title":"Closepact API","version":"1.0.0","summary":"Deals, deal rooms, documents and e-signing, booking and invoicing — for your own integrations.","description":"The Closepact REST API gives an integration the same data your team works with in the panel. Every request is authenticated with an API key created under **Settings → API keys**, and acts with the permissions of the member who created the key.\n\nAll requests and responses are JSON over HTTPS. Amounts are integers in the currency's minor unit (öre for SEK). Timestamps are ISO 8601 in UTC.\n\nThe API is included from the **Väx** tier of any module. An organization without it is answered `402` with `\"feature\": \"core.api\"` on every call; its keys are kept and work again once it has the tier. Operations that need a higher tier still say which in `x-feature` (for example creating a quote needs `proposals.cpq`, Väx), and a key reaches a module only if the member who created it holds a seat in that module.","contact":{"name":"Closepact support","email":"support@closepact.com","url":"https://closepact.com/contact"},"termsOfService":"https://closepact.com/legal/terms"},"servers":[{"url":"https://api.closepact.com/v1/api","description":"Production"}],"security":[{"ApiKey":[]}],"tags":[{"name":"Account","description":"The API key itself: which organization it belongs to, who it acts as, and what the organization holds.","x-group":"Getting started"},{"name":"Articles","description":"The price list (artikelregister) invoices are built from: what you sell, its price in öre, its VAT rate, its BAS account and whether it can carry a ROT/RUT deduction. Adding an article to an invoice line copies those onto the line, so changing an article later never changes an invoice already written.\n\nThe article number is the key bookkeepers use. It is unique in the organization and stays reserved by a deleted article, so to keep a price list in step with an ERP, look the article up by number (`GET /articles?q=`) and update it, or create it when it is missing.","x-group":"Invoicing"},{"name":"Contacts","description":"The people you deal with at your customers: name, e-mail, title and phone, optionally attached to a customer. Contacts belong to the customer record every module shares, so they need no module — only the `customers.*` permissions.","x-group":"Sales"},{"name":"Customers","description":"The customer register (kundregister): the companies and private persons you quote, carry out work for and invoice. One register is shared by every module — the customer on a deal room, on a work order and on an invoice is the same record — so these endpoints need no module, only the member's customer permissions.\n\nA customer carries its invoicing defaults (payment terms, delivery, currency, late-payment interest) and any number of addresses. Contacts and ROT/RUT tax profiles are shown on a single customer but are not managed here.\n\nSyncing with an ERP or CRM: send your system's customer number as `customer_number` when you create the customer, and look customers up with `q`, which matches the customer number and the organisationsnummer as well as the name.","x-group":"Customers"},{"name":"Deals","description":"The opportunities in your pipeline: what is being sold, to whom, for how much, and how far it has come. A deal moves through stages (`draft` → `contract_signed`) while it is open, and is closed by marking it won or lost.","x-group":"Sales"},{"name":"Documents","description":"Files you send for reading or signing: upload a contract, add recipients, send it, follow what happens to it and download it again. A document is core to every Closepact plan — no module is needed.\n\n**The usual integration**\n\n1. `POST /documents` — upload the PDF (multipart). It is processed in the background; `status` goes from `processing` to `ready`.\n2. `POST /documents/{document}/send` — name the recipients. Each gets an email with a private link; the document's `lifecycle_status` becomes `sent`.\n3. Wait for the `signature.completed` webhook (or poll `GET /documents/{document}` until `lifecycle_status` is `signed`).\n4. `GET /documents/{document}/download-link` — a short-lived URL to fetch the file.\n\n**Two statuses.** `status` is about the file: `processing` → `ready`, or `failed` when the virus scan rejects it or processing gives up (`quarantined` is reserved). `lifecycle_status` is about the recipients: `draft` → `sent` → `opened` → `signed`, with `expired` and `canceled` as other endings. The happy path only moves forward, and once a terminal status is reached only another terminal status replaces it — with one exception: sending the document again starts a new round at `sent`, from any status except `signed`.\n\n**Webhooks** for this area: `signature.requested` (sent), `document.viewed` (a recipient opened their link), `signer.signed` (one recipient signed), `signature.completed` (everyone who must sign has) and `signature.canceled`. Each carries the `document_id` and the signature request's id; see the Webhooks section for the payloads.","x-group":"Documents & e-signing"},{"name":"Employees","description":"The people who can be sent to jobs (personal), their weekly working hours and their skills. Sync them from an HR or payroll system; match on `employee_number`.","x-group":"Booking"},{"name":"Invoices","description":"Customer invoices (kundfakturor) and credit notes, from draft to paid.\n\n**Life of an invoice.** An invoice starts as a `draft`: no number, no OCR reference, freely editable, and deletable without leaving a trace. `POST /invoices/{invoice}/send` issues it — it takes the next number in its series, gets an OCR reference, is dated, has its PDF stored and is emailed — and from that moment it can never be edited or deleted. Payments then move it to `partially_paid` and `paid`. A mistake on an issued invoice is corrected with a credit note, never with an edit; that is what bokföringslagen means by an invoice.\n\n| status | meaning | can become |\n|---|---|---|\n| `draft` | being written | `sent`, `cancelled` |\n| `sent` | issued, nothing paid | `partially_paid`, `paid`, `overdue`, `credited`, `written_off` |\n| `overdue` | due date passed with money outstanding (set by a nightly job) | `partially_paid`, `paid`, `credited`, `written_off` |\n| `partially_paid` | some money in | `paid`, `overdue`, `credited`, `written_off` |\n| `paid` | the customer's share is settled | `credited`, `partially_paid` (refund) |\n| `credited` | fully credited by credit notes | — |\n| `cancelled`, `written_off` | closed | — |\n\n**Numbers and OCR.** Invoice numbers come from an unbroken series per organization and series letter (`A` unless you choose another); credit notes have their own series, `K` + the invoice's (`KA`). A number is spent only at send, inside the same transaction, so a failed send leaves no gap. The OCR reference is the invoice number's digits plus a Luhn (modulus 10) check digit — invoice 1042 gets OCR `10421` — so the invoice behind an OCR number is its digits minus the last one.\n\n**Amounts** are integers in öre, VAT is 25, 12, 6 or 0 percent per line, and the total is rounded to whole kronor (öresavrundning, shown as `rounding_cents`).\n\n**ROT and RUT.** Set `rot_rut` on the invoice and mark labour lines `rot_rut_eligible`; Closepact adds and maintains the deduction line itself — 30 % (ROT) or 50 % (RUT) of the eligible labour including VAT. Materials never qualify, whatever the flag says. The customer pays `rot_rut.customer_pays_cents`; the rest is claimed from Skatteverket in Closepact's deduction workflow, which needs the buyer's personnummer and, for ROT, the property — see the customer's `tax_profiles`. Each labour line (or the invoice, for every line that names none) can carry `rot_rut_work_type`, Skatteverket's category for the work, which the claim declares hours and material under. The yearly ceiling per person is not applied (it depends on other companies' claims), and the response says so with `annual_ceiling_applied: false`. Once the customer has paid their share, the claim is filed from the panel as Skatteverket's import file; when Skatteverket's decision is recorded, `rot_rut.status` follows it, what it paid arrives as a `rot_rut` payment, and what it refused (`rot_rut.refused_cents`) is owed by the customer again.\n\n**Webhooks.** No webhook is sent for invoices or payments yet. To follow status changes, poll `GET /invoices` with `filter[status]` or `filter[overdue]`.\n\n**Not available through the API:** sending payment reminders (deliberately a person's decision — level 3 announces collection), bank statement import and matching, recurring invoice schedules, filing ROT/RUT claims with Skatteverket, the SIE export and the reports. They are in the Closepact panel.","x-group":"Invoicing"},{"name":"Invoice lines","description":"The rows of an invoice. Lines can be added, changed, reordered and removed while the invoice is a draft; every change recalculates the invoice's totals and its ROT/RUT deduction line. On an issued invoice every write answers 409.","x-group":"Invoicing"},{"name":"Invoice payments","description":"Money received against an issued invoice. Every receipt is its own row and the invoice's `paid_cents` is their sum, so part payments are ordinary and a refund is a negative payment. Recording a payment moves the invoice to `partially_paid` or `paid` by itself.\n\nTo record a payment from a bank file, find the invoice by its OCR reference (drop the last digit and search `GET /invoices?q=`), then post the amount with the bank's reference. The OCR reference is built from the number alone, so an organization that uses several series should also check `references.ocr_reference` and the series of the match.\n\nA payment recorded here with `method` `bank` or `autogiro` is not paid a second time when the same transfer later arrives in a bank statement imported in the panel: a statement line carrying the invoice's OCR reference and exactly the payment's amount, booked within 31 days of `paid_at`, becomes that payment's bank record (its `bank_statement_line_id`), and so does a line somebody matches to the invoice by hand.","x-group":"Invoicing"},{"name":"Products","description":"The product catalogue quotes are built from: what you sell, its SKU, unit price in öre, whether it recurs, and any custom fields (properties) your organization defined for products.\n\nChanging the catalogue is included from the **Väx** tier of proposals (`proposals.cpq`); on Start it is read-only.","x-group":"Sales"},{"name":"Proposals","description":"Written proposals, edited in the panel's editor as a tree of blocks (headings, paragraphs, lists, images, pricing tables…). Every edit of the content is kept as a numbered version.","x-group":"Sales"},{"name":"Quotes","description":"Priced offers built from lines — from your product catalogue or free text — with VAT on each line, an optional quote-level discount and an optional ROT/RUT estimate. A sent quote gets its own link (`share_url`) where the buyer can accept or reject it without a deal room; a quote placed in a deal room can also be answered by the room's stakeholders.\n\nTotals are always computed by Closepact, in öre, by the same rules as an invoice. Every amount is rounded half up exactly once per line, and totals are sums of the rounded lines:\n\n- a line's `line_total_cents` is `quantity × unit_price_cents` less the line's `discount_percent`; `quantity` may have up to three decimals (2.5 hours);\n- its `net_cents` additionally carries the quote's `discount_percent` (10 % on a line already 25 % off is 32.5 % off), and its `vat_cents` is `vat_rate` of that net — 25, 12, 6 or 0, inherited from the product unless you set it;\n- `subtotal_cents` is the sum of line totals, `discount_cents` what the quote discount took off them, `tax_cents` the sum of line VAT, and `total_cents` net plus VAT; `vat_breakdown` gives net and VAT per rate.\n\nOnly lines that count are summed: every fixed line, and an optional line (`is_optional`) only while `is_selected`. The buyer ticks optional lines in or out when accepting.\n\n`rot_rut` estimates a ROT (30 %) or RUT (50 %) deduction on the `labour` lines including VAT — materials never qualify — and what the buyer pays after it. It is preliminary: the yearly ceiling per person is not applied.\n\nCreating and changing quotes is included from the **Väx** tier of proposals (`proposals.cpq`); on Start those operations answer `402` and existing quotes stay readable.\n\nAn accepted quote is handed over — to a work order waiting to be planned when you have the scheduling module, otherwise to an invoice draft when you have invoicing — automatically on acceptance or with [Hand over an accepted quote](/developers/reference/quotes#convertQuote), as each quote's `handover` says. That is included from the **Väx** tier of proposals (`proposals.convert`).","x-group":"Sales"},{"name":"Recurrences","description":"Standing agreements (återkommande avtal): a customer, a template job and an iCalendar rule. Closepact writes the jobs ahead of time every night, once per date, and they can be generated on demand.\n\nRecurring jobs are included from the **Väx** tier of booking (`booking.recurring`). Below it, writing an agreement answers `402`, existing agreements stay readable, and no jobs are generated from them.","x-group":"Booking"},{"name":"Rooms","description":"Deal rooms: the branded page a buyer opens to read your offer, watch videos, download documents and accept a quote. A room is drafted, filled with content, given stakeholders, and published; it stays open to buyers until it expires.\n\nCreating and changing rooms is included in the **Pro** tier of proposals (`proposals.rooms`); on a lower tier those operations answer `402` and the rooms already written stay readable.","x-group":"Sales"},{"name":"Room stakeholders","description":"The buyer-side people invited into a room. Each is identified by e-mail and receives a personal link to the room.","x-group":"Sales"},{"name":"Room contents","description":"The blocks a room is built from — text, links, videos, documents, proposals and quotes — arranged in sections.","x-group":"Sales"},{"name":"Services","description":"The service catalogue (tjänster): price, VAT rate, ROT/RUT eligibility, usual duration and the skills each service requires.","x-group":"Booking"},{"name":"Signature requests","description":"A signature request (an envelope) is one round of sending a document for signing: who must sign, in which order, by when, and the tamper-evident audit trail of everything that happened to it.\n\n`POST /documents/{document}/send` creates one for you and is the recommended way to send. Use these endpoints to follow a request in detail, to download its audit trail, and to cancel it.\n\n**Status.** `sent` → `partially_signed` (some have signed) → `completed` (every signer has signed). It ends as `declined` when a signer declines, `expired` when `expires_at` passes (an hourly sweep, and signing is refused the moment it passes), or `canceled` when it is canceled or replaced by a new send.\n\n**Webhooks.** `signature.requested` when it is created, `document.viewed` when a signer first opens their link, `signer.signed` for each signature, `signature.completed` when the last one is in, and `signature.canceled`. A decline or an expiry sends no webhook — watch `status`.\n\n**How recipients sign.** Each signer opens their private link in a browser — no account needed. When the document's `signer_otp_required` is on (the default for new documents), they first enter a six-digit code emailed to their address. They then sign with a typed or drawn signature (`signature_type`), accepting the terms, or decline with a reason, which the sender is emailed. Time, IP address, user agent and the SHA-256 of the file are recorded; a file that changed after sending can no longer be signed. When the last signer signs, a completion certificate is issued and every party is emailed links to the signed document and the certificate. BankID signing is not part of this flow.","x-group":"Documents & e-signing"},{"name":"Templates","description":"Reusable starting points for deal rooms, documents, proposals, mutual action plans and emails. The list holds the organization's own templates and Closepact's built-in library, which every organization shares.","x-group":"Documents & e-signing"},{"name":"Time entries","description":"Reported time (tidrapporter): clocked in and out in the field app, approved in the office, and read here for payroll. Only approved time is billed.\n\nLife of an entry: `open` (clocked in) → `submitted` (clocked out) → `approved`, or `rejected` with a reason.","x-group":"Booking"},{"name":"Webhooks","x-group":"Webhooks","description":"Webhooks tell your system the moment something happens in Closepact — a deal is won, a document is signed, a work order is completed, an invoice is issued — so you do not have to poll. Register an HTTPS URL and the events it wants; Closepact sends each one as a signed JSON `POST`.\n\nManaging endpoints needs an API key with the **admin** scope, for reading too: endpoints hand out signing secrets and their deliveries carry event data. Creating, changing, testing or deleting an endpoint also needs the member the key acts as to hold *Manage outbound webhooks* (`webhooks.manage`).\n\n**Delivery.** Answer with any `2xx` status within 15 seconds. Anything else — another status, a redirect (redirects are not followed), a timeout, a refused connection — is a failed attempt and is retried: 7 attempts in all, 5 seconds, 30 seconds, 5 minutes, 1 hour, 6 hours and 24 hours apart (about 31 hours end to end), after which the delivery is `abandoned`. Every attempt shows under the endpoint's deliveries. Closepact only connects to public internet addresses: a host that resolves to a loopback, private, link-local or otherwise reserved address is not sent to, and the delivery is `abandoned` at once. Pausing an endpoint stops new events reaching it; retries already scheduled still run.\n\n**Verify every request** with the `X-Signature` header before trusting it — see *Verifying signatures* (`x-signature` below).\n\n**Duplicates.** A delivery can arrive more than once — for example when your server processed it but answered after the timeout. Make your handler idempotent: deduplicate on `event_id`, which is the same for every endpoint that receives the event, or on the `X-Delivery-Id` header, which is unique per endpoint.\n\n**Order.** Events are delivered independently and retries interleave with newer events, so the order of arrival is not guaranteed. Use `created`, and fetch the record from the API when you need its current state.\n\n**Answer first, work later.** Acknowledge with a `2xx` straight away and do the processing on your own queue; slow handlers time out and are retried.\n\n**Payloads grow.** New fields can be added to any event without notice; ignore fields you do not recognise. Fields are never renamed or removed without a new event name.","x-signature":{"header":"X-Signature","format":"t=<timestamp>, v1=<signature>","example":"t=1792314846, v1=fa94cda11bea7d9dd2b7056fb63c376231e5a50275df0544f4aec2adcb461fa1","algorithm":"HMAC-SHA256","key":"The endpoint's signing secret: the whole string, including the `whsec_` prefix, as UTF-8 bytes. It is shown once, when the endpoint is created or its secret rotated.","signed_payload":"{t}.{raw request body}","signature_encoding":"lowercase hexadecimal (64 characters)","timestamp":"Unix time in seconds at which this attempt was signed. Every retry is signed afresh, so `t` changes between attempts while the body stays the same.","tolerance_seconds":300,"comparison":"constant-time","worked_example":{"secret":"whsec_example","timestamp":1792314846,"body":"{\"event\":\"ping\"}","signed_payload":"1792314846.{\"event\":\"ping\"}","header":"t=1792314846, v1=fa94cda11bea7d9dd2b7056fb63c376231e5a50275df0544f4aec2adcb461fa1"},"steps":["Read the raw request body exactly as received, before any JSON parsing — re-serialising parsed JSON changes the bytes and the signature will not match.","Split the X-Signature header on commas, trim the spaces, and read the values of `t` and `v1`.","Compute HMAC-SHA256 over the string `t + \".\" + body`, keyed with the endpoint's signing secret, and hex-encode it.","Compare your digest with `v1` using a constant-time comparison. Reject the request if they differ.","Reject the request if `t` is more than 300 seconds away from your clock, in either direction, so a captured request cannot be replayed later."]},"x-delivery":{"method":"POST","content_type":"application/json","user_agent":"Closepact-Webhooks/1.0","headers":{"X-Signature":"The signature, `t=<timestamp>, v1=<signature>`.","X-Event":"The event name, as in the body's `event`.","X-Event-Id":"The event id, as in the body's `event_id`.","X-Delivery-Id":"This delivery's id: one per event per endpoint, unchanged across retries. The `id` in GET /webhooks/{endpoint}/deliveries."},"timeout_seconds":15,"connect_timeout_seconds":10,"success":"Any 2xx status. The response body is ignored (the first 2,000 characters are kept for the deliveries list).","redirects":"Not followed. A 3xx counts as a failed attempt.","max_attempts":7,"retry_delays_seconds":[5,30,300,3600,21600,86400],"after_last_attempt":"The delivery is marked `abandoned` and not retried again.","address_check":"Before every attempt the endpoint's host name is resolved, and the request is sent only if every address it resolves to is public; it then goes to the address that was checked. A host that resolves to a loopback, private, link-local or otherwise reserved address is not sent to: the delivery is `abandoned` at once, with the reason in `error`, and not retried. A host that does not resolve at all is a failed attempt and is retried.","ordering":"Not guaranteed.","deduplicate_on":"`event_id` (or the X-Delivery-Id header, per endpoint)."}},{"name":"Webhook events","x-group":"Webhooks","description":"Every event Closepact sends, with the exact body. Each request is a JSON object with four fields — `event`, `event_id`, `created` and `data` — where `data` depends on the event and is described below. Subscribe an endpoint to the names listed here, or to `*` for every event, including ones added later.\n\nAll ids are UUIDs, amounts are integers in the currency's minor unit (öre for SEK), and timestamps inside `data` are ISO 8601 with offset. The records an event refers to can be fetched from the rest of this API with the same ids.\n\nEvents sent by an automation's *Send webhook* step carry the event name chosen in the automation and the data the automation ran with; they are not described here."},{"name":"Work orders","description":"Jobs (uppdrag): create them from a webshop or CRM, follow them through their statuses, complete them from the field with checklist, materials and signature, and read what they are worth.\n\nA job's life: `booked` → `scheduled` (somebody assigned) → `in_progress` → `completed` → `invoiced`, or `cancelled` before it is done. All times are UTC instants. Times of day that people set — a recurrence's `start_time`, an employee's working hours — are Swedish local time (`Europe/Stockholm`), and schedule warnings compare a job with them on that clock.","x-group":"Booking"},{"name":"Schedule","description":"Placing work: assign employees to jobs, move jobs in time, and find double bookings. Every placement is checked against the employee's other jobs, absences, working hours and employment.","x-group":"Booking"}],"paths":{"/me":{"get":{"operationId":"getMe","tags":["Account"],"summary":"Inspect the API key","description":"Returns the key, the organization it belongs to, the member it acts as and the modules the organization holds. Make this your first call: it confirms the key works and shows what it can reach.","x-scope":"read","x-module":null,"x-permissions":[],"responses":{"200":{"description":"The key and what it can reach.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Me"}},"additionalProperties":false},"example":{"data":{"api_key":{"id":"01a14f2c-7c1e-7d0b-9a3e-3f5b2c8d1e40","name":"CRM sync","prefix":"cp_live_Hx3p","scopes":["read","write"],"rate_limit_per_minute":120,"expires_at":null},"organization":{"id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","name":"Nordisk Fastighetsservice AB"},"acting_as":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se"},"modules":[{"key":"proposals","tier":"pro","status":"active","writable":true,"has_seat":true},{"key":"booking","tier":"grow","status":"trialing","writable":true,"has_seat":false}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/articles":{"get":{"operationId":"listArticles","tags":["Articles"],"summary":"List articles","description":"Articles in article-number order, the order of a printed price list. Deleted articles are not listed; deactivated ones are, unless you filter on `filter[active]=true`.\n\n`q` is a case-insensitive *contains* match on the name and the article number, so `1042` finds `ART-1042`.\n\nPaging is by cursor: pass `meta.next_cursor` back as `cursor` together with the same filters and `limit` — the URLs in `links` do not carry your filters.","x-scope":"read","x-module":"invoicing","x-permissions":[],"parameters":[{"name":"q","in":"query","required":false,"description":"Search the name and the article number.","schema":{"type":"string"},"example":"städ"},{"name":"filter[type]","in":"query","required":false,"description":"One type, or several as `filter[type][]=hour&filter[type][]=service`.","schema":{"oneOf":[{"type":"string","enum":["goods","service","hour","fee"]},{"type":"array","items":{"type":"string","enum":["goods","service","hour","fee"]}}]}},{"name":"filter[active]","in":"query","required":false,"description":"`true` for articles that can be picked for new lines, `false` for deactivated ones.","schema":{"type":"boolean"}},{"name":"filter[rot_rut_eligible]","in":"query","required":false,"description":"Only articles that can, or cannot, carry a ROT/RUT deduction.","schema":{"type":"boolean"}},{"name":"filter[account_code]","in":"query","required":false,"description":"Only articles booked to this BAS account.","schema":{"type":"string"},"example":"3001"},{"name":"limit","in":"query","required":false,"description":"Articles per page.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of articles.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Article"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a159f8-2d6a-7b3e-9f1c-8e4a2d7b5c63","article_number":"ST-100","name":"Hemstädning","description":"Städning i hemmet enligt checklista. RUT-berättigad.","type":"hour","unit":"tim","pricing":{"unit_price_cents":45000,"currency":"SEK","vat_rate":25},"account_code":"3001","rot_rut_eligible":true,"deductible":true,"line_type":"labour","is_active":true,"metadata":[],"created_at":"2026-09-01T07:30:00+00:00","updated_at":"2026-09-01T07:30:00+00:00","deleted_at":null},{"id":"01a159f8-2d84-7c51-a2e6-3f9b1d8c4e70","article_number":"ST-210","name":"Trappstädning, per uppgång","description":null,"type":"service","unit":"st","pricing":{"unit_price_cents":95000,"currency":"SEK","vat_rate":25},"account_code":"3001","rot_rut_eligible":false,"deductible":false,"line_type":"service","is_active":true,"metadata":[],"created_at":"2026-09-01T07:31:12+00:00","updated_at":"2026-09-01T07:31:12+00:00","deleted_at":null}],"links":{"first":null,"last":null,"prev":null,"next":null},"meta":{"path":"https://api.closepact.com/v1/api/articles","per_page":50,"next_cursor":null,"prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createArticle","tags":["Articles"],"summary":"Create an article","description":"Adds an article to the price list. `article_number` and `name` are required; the rest defaults to a 25 % goods article priced at 0 kr in SEK.\n\n- **Price** is `unit_price_cents`, a whole number of öre excluding VAT: 450,00 kr is `45000`. A decimal is refused, because it is almost always kronor sent by mistake.\n- **VAT** must be 25, 12, 6 or 0; anything else is a 422 that names the four rates.\n- **Type** decides what a line built from the article becomes: `hour` → `labour`, `service` → `service`, `fee` → `fee`, `goods` → `goods`.\n- **ROT/RUT.** `rot_rut_eligible` is kept only for `hour` and `service` articles — ROT and RUT cover work, never materials — and it is cleared whenever the type stops qualifying. `deductible` in the response is the answer to rely on.\n- **Account** is the BAS sales account, e.g. `3001`. Left empty, a line built from the article gets the standard account for its VAT rate (3001, 3002, 3003).\n\nAn article number that is already taken — also by a deleted article — answers 409 naming the article that holds it.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.articles.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ArticleFields"},{"type":"object","required":["article_number","name"]}]},"example":{"article_number":"ST-100","name":"Hemstädning","description":"Städning i hemmet enligt checklista. RUT-berättigad.","type":"hour","unit":"tim","unit_price_cents":45000,"vat_rate":25,"account_code":"3001","rot_rut_eligible":true}}}},"responses":{"201":{"description":"The article, with its defaults filled in.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Article"}},"additionalProperties":false},"example":{"data":{"id":"01a159f8-2d6a-7b3e-9f1c-8e4a2d7b5c63","article_number":"ST-100","name":"Hemstädning","description":"Städning i hemmet enligt checklista. RUT-berättigad.","type":"hour","unit":"tim","pricing":{"unit_price_cents":45000,"currency":"SEK","vat_rate":25},"account_code":"3001","rot_rut_eligible":true,"deductible":true,"line_type":"labour","is_active":true,"metadata":[],"created_at":"2026-10-18T09:00:00+00:00","updated_at":"2026-10-18T09:00:00+00:00","deleted_at":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/ArticleNumberTaken"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/articles/{article}":{"parameters":[{"name":"article","in":"path","required":true,"description":"The article's id.","schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"getArticle","tags":["Articles"],"summary":"Get an article","description":"One article. A deleted article answers 404, like another organization's.","x-scope":"read","x-module":"invoicing","x-permissions":[],"responses":{"200":{"description":"The article.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Article"}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateArticle","tags":["Articles"],"summary":"Update an article","description":"Changes only the fields you send. A new price applies to lines added from now on; existing invoice lines keep the price they were given. ROT/RUT eligibility is re-derived on every update, so changing an `hour` article to `goods` takes the deduction off it. Renumbering to a number that is taken answers 409.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.articles.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticleFields"},"example":{"unit_price_cents":47500}}}},"responses":{"200":{"description":"The article as it now stands.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Article"}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/ArticleNumberTaken"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteArticle","tags":["Articles"],"summary":"Delete an article","description":"Removes the article — unless it appears on an issued invoice. Those have to be reproducible for seven years, so such an article is **deactivated** instead: it stays resolvable from the invoice lines and disappears from the articles people pick from. The response says which of the two happened and why.\n\nEither way its number stays reserved. Drafts do not count; a draft can still be changed.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.articles.manage"],"responses":{"200":{"description":"The article was deleted, or deactivated because issued invoices use it.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ArticleDeletion"}},"additionalProperties":false},"examples":{"deleted":{"summary":"Not used on any issued invoice","value":{"data":{"deleted":true,"deactivated":false,"issued_invoice_lines":0,"reason":null}}},"deactivated":{"summary":"Used on issued invoices","value":{"data":{"deleted":false,"deactivated":true,"issued_invoice_lines":14,"reason":"ST-100 appears on 14 issued invoice line(s) and cannot be removed; it has been deactivated instead."}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/contacts":{"get":{"operationId":"listContacts","tags":["Contacts"],"summary":"List contacts","description":"Returns up to 200 contacts, ordered by name, each with how many signature requests and deal rooms they appear in (matched on e-mail within your organization).\n\nThis list is not paginated: when you have more than 200 contacts, narrow it with `q`. `status=inactive` lists deleted contacts instead of live ones.","x-scope":"read","x-module":null,"x-permissions":[],"parameters":[{"name":"q","in":"query","required":false,"description":"Text the name or e-mail contains, case-insensitively.","schema":{"type":"string"},"example":"solglantan.se"},{"name":"status","in":"query","required":false,"description":"`active` for live contacts, `inactive` for deleted ones. Any other value lists live contacts.","schema":{"type":"string","enum":["active","inactive"],"default":"active"}}],"responses":{"200":{"description":"The contacts.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","maxItems":200,"items":{"$ref":"#/components/schemas/Contact"}}},"additionalProperties":false},"example":{"data":[{"id":"01a15d02-6e1f-7b44-a0c9-3d7e5f1b2a96","name":"Henrik Ny","email":"henrik.ny@solglantan.se","title":"Ordförande","phone":"+46 70 123 45 67","account":{"id":"01a15b90-2d44-7c1a-8e5f-9b3c7d2e1a08","name":"Brf Solgläntan"},"documents_count":2,"rooms_count":1,"created_at":"2026-09-30T07:45:12+00:00","updated_at":"2026-10-11T15:20:31+00:00"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createContact","tags":["Contacts"],"summary":"Create a contact","description":"Creates a contact. E-mail addresses are not required to be unique, so creating the same person twice gives two contacts: look them up with `q` first when you sync from another system.\n\nNot idempotent. No webhook fires.","x-scope":"write","x-module":null,"x-permissions":["customers.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","email"],"properties":{"name":{"type":"string","minLength":1,"maxLength":255},"email":{"type":"string","format":"email","maxLength":255},"title":{"type":["string","null"],"maxLength":128,"description":"Job title."},"phone":{"type":["string","null"],"maxLength":32},"account_id":{"type":["string","null"],"format":"uuid","description":"A customer in this organization."}},"additionalProperties":false},"example":{"name":"Henrik Ny","email":"henrik.ny@solglantan.se","title":"Ordförande","phone":"+46 70 123 45 67","account_id":"01a15b90-2d44-7c1a-8e5f-9b3c7d2e1a08"}}}},"responses":{"201":{"description":"The contact, as created.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactEnvelope"},"example":{"data":{"id":"01a15d02-6e1f-7b44-a0c9-3d7e5f1b2a96","name":"Henrik Ny","email":"henrik.ny@solglantan.se","title":"Ordförande","phone":"+46 70 123 45 67","account":{"id":"01a15b90-2d44-7c1a-8e5f-9b3c7d2e1a08","name":"Brf Solgläntan"},"documents_count":0,"rooms_count":0,"created_at":"2026-10-18T08:02:11+00:00","updated_at":"2026-10-18T08:02:11+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/contacts/{contact}":{"parameters":[{"$ref":"#/components/parameters/ContactId"}],"get":{"operationId":"getContact","tags":["Contacts"],"summary":"Retrieve a contact","description":"Returns one live contact, in the same shape as the list. A deleted contact is a 404 here; list it with `status=inactive`.","x-scope":"read","x-module":null,"x-permissions":[],"responses":{"200":{"description":"The contact.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactEnvelope"},"example":{"data":{"id":"01a15d02-6e1f-7b44-a0c9-3d7e5f1b2a96","name":"Henrik Ny","email":"henrik.ny@solglantan.se","title":"Ordförande","phone":"+46 70 123 45 67","account":{"id":"01a15b90-2d44-7c1a-8e5f-9b3c7d2e1a08","name":"Brf Solgläntan"},"documents_count":2,"rooms_count":1,"created_at":"2026-09-30T07:45:12+00:00","updated_at":"2026-10-11T15:20:31+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateContact","tags":["Contacts"],"summary":"Update a contact","description":"Changes the fields you send and leaves the rest alone. Send `null` to clear `title`, `phone` or `account_id`.\n\nChanging the e-mail changes which signature requests and rooms are counted for the contact — the counts follow the address, not the contact. No webhook fires.","x-scope":"write","x-module":null,"x-permissions":["customers.update"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255},"email":{"type":"string","format":"email","maxLength":255},"title":{"type":["string","null"],"maxLength":128},"phone":{"type":["string","null"],"maxLength":32},"account_id":{"type":["string","null"],"format":"uuid","description":"A customer in this organization, or null to detach the contact."}},"additionalProperties":false},"example":{"title":"Kassör","phone":null}}}},"responses":{"200":{"description":"The contact, as it is now.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactEnvelope"},"example":{"data":{"id":"01a15d02-6e1f-7b44-a0c9-3d7e5f1b2a96","name":"Henrik Ny","email":"henrik.ny@solglantan.se","title":"Kassör","phone":null,"account":{"id":"01a15b90-2d44-7c1a-8e5f-9b3c7d2e1a08","name":"Brf Solgläntan"},"documents_count":2,"rooms_count":1,"created_at":"2026-09-30T07:45:12+00:00","updated_at":"2026-10-18T08:10:40+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteContact","tags":["Contacts"],"summary":"Delete a contact","description":"Deletes the contact. It leaves the live list and lookups at once, and is still listed with `status=inactive`. Stakeholders and signers with the same e-mail are not touched. No webhook fires.","x-scope":"write","x-module":null,"x-permissions":["customers.delete"],"responses":{"204":{"description":"The contact was deleted. No body."},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/customers":{"get":{"operationId":"listCustomers","tags":["Customers"],"summary":"List customers","description":"Customers in alphabetical order, with their addresses, one page at a time.\n\n`q` is a case-insensitive *contains* match on the name, the customer number and the organisationsnummer, so `769612` finds Brf Solglantan and `1042` finds customer 1042. Filters combine with AND.\n\nPaging is by cursor: pass `meta.next_cursor` back as `cursor`, together with the same `q`, filters and `limit` — the URLs in `links` do not carry your filters.\n\nContacts and tax profiles are only included when you fetch a single customer.","x-scope":"read","x-module":null,"x-permissions":[],"parameters":[{"name":"q","in":"query","required":false,"description":"Search the name, customer number and organisationsnummer.","schema":{"type":"string"},"example":"Solglantan"},{"name":"filter[customer_type]","in":"query","required":false,"description":"Only companies, or only private persons.","schema":{"type":"string","enum":["company","private"]}},{"name":"filter[blocked]","in":"query","required":false,"description":"`true` for customers your office has blocked from further invoicing, `false` for the rest.","schema":{"type":"boolean"}},{"name":"limit","in":"query","required":false,"description":"Customers per page.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of customers.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Customer"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a15a20-3b7e-7f41-9d0c-2a6f4b8e1c55","name":"Brf Solglantan","customer_number":"1042","customer_type":"company","org_number":"769612-3456","vat_number":null,"domain":"solglantan.se","industry":"Bostadsrättsförening","industry_code":"68203","country":"SE","invoicing":{"payment_terms_days":30,"late_interest_percent":10.5,"vat_mode":"se","delivery":"email","peppol_id":null,"reference":"Styrelsen / Karin Holm","reminder_email":"ekonomi@solglantan.se","currency":"SEK","credit_limit_cents":null,"is_blocked":false},"locale":"sv","notes":"Portkod 4512. Trappstädning varannan tisdag.","sub_entity_id":null,"addresses":[{"id":"01a15a20-3b9a-7a12-8f3e-5c1d7e9a2b64","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","account_id":"01a15a20-3b7e-7f41-9d0c-2a6f4b8e1c55","type":"billing","label":"Fakturaadress","street":"Box 1123","street2":null,"zip":"165 26","city":"Hässelby","country":"SE","lat":null,"lng":null,"geofence_radius_m":null,"access_notes":null,"contact_id":null,"is_default":true,"metadata":[],"created_at":"2026-09-02T08:14:11.000000Z","updated_at":"2026-09-02T08:14:11.000000Z","deleted_at":null}],"created_at":"2026-09-02T08:14:11+00:00","updated_at":"2026-10-01T12:40:03+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":"https://api.closepact.com/v1/api/customers?cursor=eyJuYW1lIjoiQnJmIFNvbGdsYW50YW4iLCJfcG9pbnRzVG9OZXh0SXRlbXMiOnRydWV9"},"meta":{"path":"https://api.closepact.com/v1/api/customers","per_page":25,"next_cursor":"eyJuYW1lIjoiQnJmIFNvbGdsYW50YW4iLCJfcG9pbnRzVG9OZXh0SXRlbXMiOnRydWV9","prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createCustomer","tags":["Customers"],"summary":"Create a customer","description":"Adds a customer to the register. Only `name` is required; everything else falls back to the organization's defaults, which the response shows (30 days' payment terms, Swedish VAT, email delivery, SEK, Swedish).\n\n**Customer number.** Leave `customer_number` out and the next free number in the organization's series is assigned (1001, 1002, …). When syncing from an ERP, send the ERP's number instead so both systems agree. It must be unique in the organization; unlike an invoice number it carries no legal weight and gaps do not matter.\n\n**Company or private person.** `customer_type` decides which tax deductions can ever apply: RUT is for private persons, ROT for private persons who own the home (a villa, or a bostadsrätt through its BRF). For a private person leave `org_number` empty — a personnummer does not belong in a free-text field. What Skatteverket needs for a ROT/RUT claim lives in the customer's tax profiles, which store only a hash and the last four digits.\n\nAddresses are added afterwards with `POST /customers/{account}/addresses`.","x-scope":"write","x-module":null,"x-permissions":["customers.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/CustomerFields"},{"type":"object","required":["name"]}]},"example":{"name":"Brf Solglantan","customer_number":"1042","customer_type":"company","org_number":"769612-3456","country":"SE","payment_terms_days":30,"reminder_email":"ekonomi@solglantan.se","notes":"Portkod 4512. Trappstädning varannan tisdag."}}}},"responses":{"201":{"description":"The customer, with every default filled in.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Customer"}},"additionalProperties":false},"example":{"data":{"id":"01a15a20-3b7e-7f41-9d0c-2a6f4b8e1c55","name":"Brf Solglantan","customer_number":"1042","customer_type":"company","org_number":"769612-3456","vat_number":null,"domain":null,"industry":null,"industry_code":null,"country":"SE","invoicing":{"payment_terms_days":30,"late_interest_percent":null,"vat_mode":"se","delivery":"email","peppol_id":null,"reference":null,"reminder_email":"ekonomi@solglantan.se","currency":"SEK","credit_limit_cents":null,"is_blocked":false},"locale":"sv","notes":"Portkod 4512. Trappstädning varannan tisdag.","sub_entity_id":null,"addresses":[],"created_at":"2026-10-18T09:12:30+00:00","updated_at":"2026-10-18T09:12:30+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/customers/{account}":{"parameters":[{"name":"account","in":"path","required":true,"description":"The customer's id. (Customers are `account` records, which is why the parameter has this name.)","schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"getCustomer","tags":["Customers"],"summary":"Get a customer","description":"One customer with its addresses, its contacts and its ROT/RUT tax profiles.\n\n**Tax profiles** hold what a ROT or RUT claim needs: the buyer (only the last four digits of the personnummer are ever returned; the full number is stored as a hash), and for ROT the property — a `property_designation` (fastighetsbeteckning) for a house, or `brf_org_number` plus `apartment_number` (lägenhetsnummer) for a bostadsrätt. Two owners sharing a claim show up as a co-applicant and an `applicant_share_percent`. Closepact does not refuse to send a ROT/RUT invoice to a customer without one, so check here first if your integration creates such invoices.\n\nAnother organization's customer id answers 404.","x-scope":"read","x-module":null,"x-permissions":[],"responses":{"200":{"description":"The customer.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Customer"}},"additionalProperties":false},"example":{"data":{"id":"01a15a21-0c44-7b19-8e2d-6f3a9c1b5d70","name":"Eva Ek","customer_number":"1043","customer_type":"private","org_number":null,"vat_number":null,"domain":null,"industry":null,"industry_code":null,"country":"SE","invoicing":{"payment_terms_days":20,"late_interest_percent":null,"vat_mode":"se","delivery":"email","peppol_id":null,"reference":null,"reminder_email":null,"currency":"SEK","credit_limit_cents":null,"is_blocked":false},"locale":"sv","notes":null,"sub_entity_id":null,"addresses":[{"id":"01a15a21-0c61-7d02-a4b8-2e9f7c3a1d56","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","account_id":"01a15a21-0c44-7b19-8e2d-6f3a9c1b5d70","type":"service","label":"Lägenhet 1101","street":"Solgläntans väg 12","street2":"4 tr","zip":"165 72","city":"Hässelby","country":"SE","lat":59.3661,"lng":17.8329,"geofence_radius_m":75,"access_notes":"Nyckel i nyckelbox vid porten, kod 1937.","contact_id":null,"is_default":true,"metadata":[],"created_at":"2026-09-14T10:03:51.000000Z","updated_at":"2026-09-14T10:03:51.000000Z","deleted_at":null}],"contacts":[{"id":"01a15a21-0c7a-70e4-9b3c-5d8a2f6e1c49","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","account_id":"01a15a21-0c44-7b19-8e2d-6f3a9c1b5d70","email":"eva.ek@example.se","name":"Eva Ek","title":null,"phone":"+46 70 123 45 67","crm_external_id":null,"crm_provider":null,"metadata":null,"created_at":"2026-09-14T10:03:51.000000Z","updated_at":"2026-09-14T10:03:51.000000Z","deleted_at":null}],"tax_profiles":[{"id":"01a15a21-0c93-7a58-b1e6-8c4d2f9a3e17","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","account_id":"01a15a21-0c44-7b19-8e2d-6f3a9c1b5d70","address_id":"01a15a21-0c61-7d02-a4b8-2e9f7c3a1d56","label":"Bostadsrätt i Brf Solglantan","personal_number_last4":"1234","property_designation":null,"apartment_number":"1101","brf_org_number":"769612-3456","co_applicant_name":null,"co_applicant_personal_number_last4":null,"applicant_share_percent":100,"created_at":"2026-09-14T10:05:12.000000Z","updated_at":"2026-09-14T10:05:12.000000Z","deleted_at":null}],"created_at":"2026-09-14T10:03:51+00:00","updated_at":"2026-09-14T10:03:51+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateCustomer","tags":["Customers"],"summary":"Update a customer","description":"Changes only the fields you send; send `null` to clear a nullable one.\n\nNew invoicing defaults apply to invoices created afterwards. An invoice that already exists keeps the payment terms and currency it was created with, and an issued invoice never changes at all.","x-scope":"write","x-module":null,"x-permissions":["customers.update"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerFields"},"example":{"payment_terms_days":20,"late_interest_percent":10.5,"reminder_email":"ekonomi@solglantan.se"}}}},"responses":{"200":{"description":"The customer as it now stands.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Customer"}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteCustomer","tags":["Customers"],"summary":"Delete a customer","description":"Removes the customer from the register. The record is soft-deleted: it disappears from every list and lookup, but the invoices, quotes and work orders that name it are kept, and so is its customer number — a new customer cannot take it over.\n\nMembers with the Account Executive role may create and edit customers but not delete them; deleting is `customers.delete`.","x-scope":"write","x-module":null,"x-permissions":["customers.delete"],"responses":{"200":{"description":"The customer was deleted.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"boolean","enum":[true]}},"additionalProperties":false},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/customers/{account}/addresses":{"parameters":[{"name":"account","in":"path","required":true,"description":"The customer's id.","schema":{"type":"string","format":"uuid"}}],"post":{"operationId":"createCustomerAddress","tags":["Customers"],"summary":"Add an address","description":"Adds a billing, postal or service address to the customer. A customer can have several of each: a housing cooperative billed at its box address with work done at three properties is one customer with one billing and three service addresses.\n\n- `type` defaults to `service` — where the work is done.\n- `is_default: true` makes this the default address of its type and takes the flag off the customer's other addresses of the same type.\n- `lat`, `lng` and `geofence_radius_m` (10–5000 m) let field staff clock in against the address. An address that has coordinates and no radius gets 100 m, and the response says so.\n\nThe response contains the fields you sent plus the ids and timestamps; column defaults you did not send (`type`, `country`, `is_default`) are applied but not echoed. Read the customer to see the complete row.","x-scope":"write","x-module":null,"x-permissions":["customers.update"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/CustomerAddressFields"},{"type":"object","required":["street"]}]},"example":{"type":"billing","label":"Fakturaadress","street":"Box 1123","zip":"165 26","city":"Hässelby","country":"SE","is_default":true}}}},"responses":{"201":{"description":"The address that was added.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/CustomerAddress"}},"additionalProperties":false},"example":{"data":{"type":"billing","label":"Fakturaadress","street":"Box 1123","zip":"165 26","city":"Hässelby","country":"SE","is_default":true,"account_id":"01a15a20-3b7e-7f41-9d0c-2a6f4b8e1c55","id":"01a15a20-3b9a-7a12-8f3e-5c1d7e9a2b64","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","updated_at":"2026-10-18T09:13:02.000000Z","created_at":"2026-10-18T09:13:02.000000Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/customers/{account}/addresses/{address}":{"parameters":[{"name":"account","in":"path","required":true,"description":"The customer's id.","schema":{"type":"string","format":"uuid"}},{"name":"address","in":"path","required":true,"description":"The address's id. It must belong to this customer, or the answer is 404.","schema":{"type":"string","format":"uuid"}}],"patch":{"operationId":"updateCustomerAddress","tags":["Customers"],"summary":"Update an address","description":"Changes only the fields you send. Setting `is_default: true` takes the flag off the customer's other addresses of the same type. If the address ends up with coordinates and no `geofence_radius_m`, the radius is set to 100 m. Answers with the complete address.","x-scope":"write","x-module":null,"x-permissions":["customers.update"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerAddressFields"},"example":{"access_notes":"Ny portkod från 1 november: 2280."}}}},"responses":{"200":{"description":"The address as it now stands.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/CustomerAddress"}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteCustomerAddress","tags":["Customers"],"summary":"Delete an address","description":"Removes the address (soft delete). Work orders and invoices that were already addressed keep what was printed on them.","x-scope":"write","x-module":null,"x-permissions":["customers.update"],"responses":{"200":{"description":"The address was deleted.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"boolean","enum":[true]}},"additionalProperties":false},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/deals":{"get":{"operationId":"listDeals","tags":["Deals"],"summary":"List deals","description":"Returns the organization's deals as a cursor-paginated list, newest first unless you ask for another order.\n\nFilters combine with AND. To match several values of one filter, repeat it with brackets: `filter[stage][]=sent&filter[stage][]=opened`. `q` searches the title, case-insensitively and with å, ä and ö folded correctly.\n\nPage through with `cursor`: pass `meta.next_cursor` from the previous page until it is `null`. `links.next` is the same request with the cursor filled in, filters included.","x-scope":"read","x-module":"proposals","x-permissions":[],"parameters":[{"name":"filter[stage]","in":"query","required":false,"description":"Only deals at this stage.","schema":{"$ref":"#/components/schemas/DealStage"}},{"name":"filter[status]","in":"query","required":false,"description":"Only deals with this status.","schema":{"$ref":"#/components/schemas/DealStatus"}},{"name":"filter[owner_id]","in":"query","required":false,"description":"Only deals owned by this member.","schema":{"type":"string","format":"uuid"}},{"name":"filter[account_id]","in":"query","required":false,"description":"Only deals for this customer.","schema":{"type":"string","format":"uuid"}},{"name":"q","in":"query","required":false,"description":"Text the deal title contains.","schema":{"type":"string"},"example":"RUT"},{"name":"sort","in":"query","required":false,"description":"Comma-separated sort fields, each optionally prefixed with `-` for descending: `created_at`, `updated_at`, `expected_close_date`, `value_cents`, `title`, `stage`. Unknown fields are ignored. `stage` sorts by the stage key alphabetically, not by pipeline order. Deals without an expected close date sort as though dated 9999-12-31, so they come last in ascending order.","schema":{"type":"string","default":"-created_at"},"example":"-value_cents,title"},{"$ref":"#/components/parameters/SalesLimit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of deals.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Deal"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","title":"Hemstäd RUT – Henrik Ny","value_cents":320000,"currency":"SEK","stage":"sent","status":"open","expected_close_date":"2026-11-30","closed_at":null,"lost_reason":null,"owner":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se","avatar_url":null},"account":{"id":"01a15b90-2d44-7c1a-8e5f-9b3c7d2e1a08","name":"Henrik Ny","domain":null},"custom_fields":{"crm_id":"HS-1042"},"created_at":"2026-10-12T08:14:03+00:00","updated_at":"2026-10-14T13:02:47+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":"https://api.closepact.com/v1/api/deals?filter%5Bstage%5D=sent&cursor=eyJjcmVhdGVkX2F0IjoiMjAyNi0xMC0xMiIsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0"},"meta":{"path":"https://api.closepact.com/v1/api/deals","per_page":25,"next_cursor":"eyJjcmVhdGVkX2F0IjoiMjAyNi0xMC0xMiIsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0","prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createDeal","tags":["Deals"],"summary":"Create a deal","description":"Creates an open deal at the stage you give. The deal is owned by `owner_id`, or by the member the key acts as when you leave it out, and the creation is recorded in the deal's history under that member.\n\nFires the `deal.created` webhook, and is pushed to the organization's connected CRM if there is one.\n\nNot idempotent: sending the same request twice creates two deals. Store the returned `id` (or keep your own id in `custom_fields`) before retrying a request whose answer you did not receive.","x-scope":"write","x-module":"proposals","x-permissions":["deals.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealCreate"},"example":{"title":"Hemstäd RUT – Henrik Ny","value_cents":320000,"currency":"SEK","stage":"draft","account_id":"01a15b90-2d44-7c1a-8e5f-9b3c7d2e1a08","expected_close_date":"2026-11-30","custom_fields":{"crm_id":"HS-1042"}}}}},"responses":{"201":{"description":"The deal, as created.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealEnvelope"},"example":{"data":{"id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","title":"Hemstäd RUT – Henrik Ny","value_cents":320000,"currency":"SEK","stage":"draft","status":"open","expected_close_date":"2026-11-30","closed_at":null,"lost_reason":null,"owner":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se","avatar_url":null},"account":{"id":"01a15b90-2d44-7c1a-8e5f-9b3c7d2e1a08","name":"Henrik Ny","domain":null},"custom_fields":{"crm_id":"HS-1042"},"created_at":"2026-10-12T08:14:03+00:00","updated_at":"2026-10-12T08:14:03+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/deals/{deal}":{"parameters":[{"$ref":"#/components/parameters/DealId"}],"get":{"operationId":"getDeal","tags":["Deals"],"summary":"Retrieve a deal","description":"Returns one deal with its owner and customer. A deal that was deleted, or that belongs to another organization, is a 404.","x-scope":"read","x-module":"proposals","x-permissions":[],"responses":{"200":{"description":"The deal.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealEnvelope"},"example":{"data":{"id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","title":"Hemstäd RUT – Henrik Ny","value_cents":320000,"currency":"SEK","stage":"engaged","status":"open","expected_close_date":"2026-11-30","closed_at":null,"lost_reason":null,"owner":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se","avatar_url":null},"account":null,"custom_fields":null,"created_at":"2026-10-12T08:14:03+00:00","updated_at":"2026-10-15T09:41:10+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateDeal","tags":["Deals"],"summary":"Update a deal","description":"Changes the fields you send and leaves the rest alone. Every field that actually changed is recorded in the deal's history, with its old and new value, under the member the key acts as.\n\nStage and status are not changed here: use [Move a deal to a stage](/developers/reference/deals#changeDealStage), [Mark a deal won](/developers/reference/deals#markDealWon) and [Mark a deal lost](/developers/reference/deals#markDealLost). Fields this endpoint does not know are ignored.\n\nA member who holds `deals.update.owned` but not `deals.update.any` may update only the deals they own; anyone else's is a 403 `forbidden`. `custom_fields` is replaced as a whole, not merged.","x-scope":"write","x-module":"proposals","x-permissions":["deals.update.any","deals.update.owned"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealUpdate"},"example":{"value_cents":450000,"expected_close_date":"2026-12-15"}}}},"responses":{"200":{"description":"The deal, as it is now.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealEnvelope"},"example":{"data":{"id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","title":"Hemstäd RUT – Henrik Ny","value_cents":450000,"currency":"SEK","stage":"engaged","status":"open","expected_close_date":"2026-12-15","closed_at":null,"lost_reason":null,"owner":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se","avatar_url":null},"account":{"id":"01a15b90-2d44-7c1a-8e5f-9b3c7d2e1a08","name":"Henrik Ny","domain":null},"custom_fields":{"crm_id":"HS-1042"},"created_at":"2026-10-12T08:14:03+00:00","updated_at":"2026-10-16T10:05:22+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteDeal","tags":["Deals"],"summary":"Delete a deal","description":"Deletes the deal. It disappears from lists and lookups at once (a later request for it is a 404); rooms, quotes and proposals linked to it are kept and keep their `deal_id`.\n\nA member who holds `deals.delete.owned` but not `deals.delete.any` may delete only the deals they own. No webhook fires.","x-scope":"write","x-module":"proposals","x-permissions":["deals.delete.any","deals.delete.owned"],"responses":{"200":{"description":"The deal was deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesDeleted"},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/deals/{deal}/stage":{"parameters":[{"$ref":"#/components/parameters/DealId"}],"post":{"operationId":"changeDealStage","tags":["Deals"],"summary":"Move a deal to a stage","description":"Moves the deal to another pipeline stage. Stages may be skipped and moved backwards; the stage is independent of the deal's status, so this works on open and closed deals alike.\n\nWhen the stage actually changes, the move is recorded in the deal's history with your `reason`, the `deal.stage_changed` webhook fires (with `from_stage`, `to_stage` and `reason`), and the change is pushed to a connected CRM. The first move to `sent` or any later stage also stamps the moment the deal was sent, which cycle-time reporting counts from.\n\nIdempotent: moving a deal to the stage it is already at changes nothing, records nothing and fires nothing.","x-scope":"write","x-module":"proposals","x-permissions":["deals.update.any","deals.update.owned"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["stage"],"properties":{"stage":{"$ref":"#/components/schemas/DealStage"},"reason":{"type":["string","null"],"maxLength":500,"description":"Why the deal moved, kept in its history and sent with the webhook."}},"additionalProperties":false},"example":{"stage":"in_negotiation","reason":"Kunden vill ha veckovis städning i stället"}}}},"responses":{"200":{"description":"The deal at its new stage.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealEnvelope"},"example":{"data":{"id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","title":"Hemstäd RUT – Henrik Ny","value_cents":320000,"currency":"SEK","stage":"in_negotiation","status":"open","expected_close_date":"2026-11-30","closed_at":null,"lost_reason":null,"owner":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se","avatar_url":null},"account":null,"custom_fields":null,"created_at":"2026-10-12T08:14:03+00:00","updated_at":"2026-10-16T14:30:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/deals/{deal}/won":{"parameters":[{"$ref":"#/components/parameters/DealId"}],"post":{"operationId":"markDealWon","tags":["Deals"],"summary":"Mark a deal won","description":"Closes the deal as won: `status` becomes `won`, `closed_at` is set to now and any `lost_reason` is cleared. The stage is left as it is. Works on a lost deal too, which is how a lost deal is won back.\n\nRecorded in the deal's history and fires the `deal.won` webhook. Idempotent: a deal that is already won is returned unchanged and nothing fires again. No request body.","x-scope":"write","x-module":"proposals","x-permissions":["deals.update.any","deals.update.owned"],"responses":{"200":{"description":"The won deal.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealEnvelope"},"example":{"data":{"id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","title":"Hemstäd RUT – Henrik Ny","value_cents":320000,"currency":"SEK","stage":"contract_signed","status":"won","expected_close_date":"2026-11-30","closed_at":"2026-10-18T09:12:44+00:00","lost_reason":null,"owner":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se","avatar_url":null},"account":{"id":"01a15b90-2d44-7c1a-8e5f-9b3c7d2e1a08","name":"Henrik Ny","domain":null},"custom_fields":null,"created_at":"2026-10-12T08:14:03+00:00","updated_at":"2026-10-18T09:12:44+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/deals/{deal}/lost":{"parameters":[{"$ref":"#/components/parameters/DealId"}],"post":{"operationId":"markDealLost","tags":["Deals"],"summary":"Mark a deal lost","description":"Closes the deal as lost: `status` becomes `lost`, `closed_at` is set to now and `lost_reason` to the reason you give. The stage is left as it is. Works on a won deal too.\n\nRecorded in the deal's history. No webhook fires for a lost deal; to notice them, list with `filter[status]=lost`. Idempotent: a deal that is already lost is returned unchanged, and a second call does not replace the reason.","x-scope":"write","x-module":"proposals","x-permissions":["deals.update.any","deals.update.owned"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":["string","null"],"maxLength":500,"description":"Why the deal was lost."}},"additionalProperties":false},"example":{"reason":"Valde en leverantör med lägre timpris"}}}},"responses":{"200":{"description":"The lost deal.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealEnvelope"},"example":{"data":{"id":"01a15c31-90fd-7a0e-b6d2-5c1e8f3a9d02","title":"Flyttstäd – Sara Ek","value_cents":540000,"currency":"SEK","stage":"sent","status":"lost","expected_close_date":null,"closed_at":"2026-10-18T09:20:05+00:00","lost_reason":"Valde en leverantör med lägre timpris","owner":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se","avatar_url":null},"account":null,"custom_fields":null,"created_at":"2026-10-02T11:00:00+00:00","updated_at":"2026-10-18T09:20:05+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/documents":{"get":{"operationId":"listDocuments","tags":["Documents"],"summary":"List documents","description":"Every document in the organization, newest first, a page at a time. List items carry the document's own fields; `owner`, `recipients`, `active_send`, `tags` and `engagement` are only returned by `GET /documents/{document}`.\n\nFilter by file type, file `status` or `lifecycle_status`. Each filter takes one value (`filter[lifecycle_status]=sent`) or several (`filter[lifecycle_status][]=sent&filter[lifecycle_status][]=opened`).\n\nPolling for signatures? Prefer the `signature.completed` webhook; if you must poll, filter on `lifecycle_status` rather than fetching every document.","x-scope":"read","x-module":null,"x-permissions":[],"parameters":[{"name":"q","in":"query","required":false,"description":"Case-insensitive match anywhere in the title.","schema":{"type":"string"},"example":"Serviceavtal"},{"name":"filter[type]","in":"query","required":false,"description":"Only documents of this file type. Repeat as `filter[type][]` for several.","schema":{"type":"string","enum":["pdf","docx","image","video","file"]}},{"name":"filter[status]","in":"query","required":false,"description":"Only documents whose file is in this processing status. Repeat as `filter[status][]` for several.","schema":{"type":"string","enum":["processing","ready","failed","quarantined"]}},{"name":"filter[lifecycle_status]","in":"query","required":false,"description":"Only documents at this point with their recipients. Repeat as `filter[lifecycle_status][]` for several.","schema":{"type":"string","enum":["draft","sent","opened","signed","declined","expired","canceled"]}},{"name":"limit","in":"query","required":false,"description":"Items per page. Values outside 1–100 are clamped.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of documents.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Document"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47","title":"Serviceavtal 2027 – Byggbolaget i Mälardalen AB","type":"pdf","mime_type":"application/pdf","size_bytes":284516,"page_count":6,"page_images_status":"ready","status":"ready","lifecycle_status":"opened","value_cents":18500000,"currency":"SEK","sent_at":"2026-10-12T08:14:03+00:00","expires_at":"2026-11-11T22:59:59+00:00","days_left":24,"checksum_sha256":"9f2c4b7e1d0a8c3f5e6b2d9a7c4e1f0b3d8a6c2e5f9b1d7a4c0e3f6b8d2a5c9e","signer_otp_required":true,"signer_verification_channel":"email","bankid_sign_required":false,"bankid_identification_required":false,"metadata":{"original_name":"Serviceavtal_2027_Byggbolaget.pdf","has_text":true,"text_length":18234,"processed_at":"2026-10-12T08:12:41+00:00"},"created_at":"2026-10-12T08:12:37+00:00","updated_at":"2026-10-13T06:51:19+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":"https://api.closepact.com/v1/api/documents?cursor=eyJjcmVhdGVkX2F0IjoiMjAyNi0xMC0xMiAwODoxMjozNyJ9"},"meta":{"path":"https://api.closepact.com/v1/api/documents","per_page":25,"next_cursor":"eyJjcmVhdGVkX2F0IjoiMjAyNi0xMC0xMiAwODoxMjozNyJ9","prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"uploadDocument","tags":["Documents"],"summary":"Upload a document","description":"Uploads a file as `multipart/form-data` and creates a document in `draft`.\n\n**Fields.** `file` (required) and `title` (optional; defaults to the file name).\n\n**Types.** Any file type is accepted. `type` is derived from the detected MIME type: `application/pdf` → `pdf`; Word and other Office formats → `docx`; `image/*` → `image`; `video/*` → `video`; anything else → `file`. Upload a **PDF** for anything that will be signed: page images — what the signing view shows and what per-page reading statistics are counted against — are only produced from PDFs.\n\n**Size.** Validation allows up to 51,200 KB (50 MiB). The server also caps the whole request at 50 MiB *including* the multipart overhead, and refuses a larger request before it reaches the API, so in practice keep files a little under the limit — 49 MB is safe.\n\n**Processing.** The response comes back at once with `status: processing`. A background job virus-scans the file, extracts its text and renders page images, then sets `status` to `ready` (or `failed` if the file is infected or processing gives up). You can add recipients and send while it is still processing.","x-scope":"write","x-module":null,"x-permissions":["documents.upload"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary","description":"The file. At most 51,200 KB (50 MiB); see Size above."},"title":{"type":"string","maxLength":255,"description":"Display title. Defaults to the uploaded file's name."}}}}}},"responses":{"201":{"description":"The document, still processing.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Document"}},"additionalProperties":false},"example":{"data":{"id":"01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47","title":"Serviceavtal 2027 – Byggbolaget i Mälardalen AB","type":"pdf","mime_type":"application/pdf","size_bytes":284516,"page_count":null,"page_images_status":"pending","status":"processing","lifecycle_status":"draft","value_cents":null,"currency":"SEK","sent_at":null,"expires_at":null,"days_left":null,"checksum_sha256":"9f2c4b7e1d0a8c3f5e6b2d9a7c4e1f0b3d8a6c2e5f9b1d7a4c0e3f6b8d2a5c9e","signer_otp_required":true,"signer_verification_channel":"email","bankid_sign_required":false,"bankid_identification_required":false,"metadata":{"original_name":"Serviceavtal_2027_Byggbolaget.pdf"},"created_at":"2026-10-12T08:12:37+00:00","updated_at":"2026-10-12T08:12:37+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/documents/{document}":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"get":{"operationId":"getDocument","tags":["Documents"],"summary":"Retrieve a document","description":"The document with everything around it: who uploaded it (`owner`), its `recipients`, the signature request currently in play (`active_send`), its `tags` and a reading summary (`engagement`).\n\nThis is the call to poll for status. `lifecycle_status` says where the document stands; `active_send.status` says the same for the signature request (`sent`, `partially_signed`, `completed`, `declined`, `expired`, `canceled`), and each recipient's `status` shows who has opened, signed or declined.\n\n`recipients` lists everyone the document has been sent to, including recipients of an earlier signature request that a later send replaced; `active_send` tells you which request is current.","x-scope":"read","x-module":null,"x-permissions":[],"responses":{"200":{"description":"The document.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Document"}},"additionalProperties":false},"example":{"data":{"id":"01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47","title":"Serviceavtal 2027 – Byggbolaget i Mälardalen AB","type":"pdf","mime_type":"application/pdf","size_bytes":284516,"page_count":6,"page_images_status":"ready","status":"ready","lifecycle_status":"opened","value_cents":18500000,"currency":"SEK","sent_at":"2026-10-12T08:14:03+00:00","expires_at":"2026-11-11T22:59:59+00:00","days_left":24,"checksum_sha256":"9f2c4b7e1d0a8c3f5e6b2d9a7c4e1f0b3d8a6c2e5f9b1d7a4c0e3f6b8d2a5c9e","signer_otp_required":true,"signer_verification_channel":"email","bankid_sign_required":false,"bankid_identification_required":false,"metadata":{"original_name":"Serviceavtal_2027_Byggbolaget.pdf","has_text":true,"text_length":18234,"processed_at":"2026-10-12T08:12:41+00:00"},"created_at":"2026-10-12T08:12:37+00:00","updated_at":"2026-10-13T06:51:19+00:00","owner":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist"},"recipients":[{"id":"01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03","name":"Erik Johansson","email":"erik.johansson@byggbolaget.se","role":"signer","status":"opened","order_index":0,"opened_at":"2026-10-13T06:48:02+00:00","signed_at":null,"declined_at":null,"decline_reason":null,"view_count":2,"total_time_ms":412000,"last_activity_at":"2026-10-13T06:51:19+00:00","reminder_count":0,"last_reminded_at":null,"phone_number":null,"bankid_required":false,"personal_number_masked":null},{"id":"01a15a31-0c2e-71aa-9c3d-6b1e8f2a4d59","name":"Maria Svensson","email":"maria.svensson@byggbolaget.se","role":"cc","status":"pending","order_index":1,"opened_at":null,"signed_at":null,"declined_at":null,"decline_reason":null,"view_count":0,"total_time_ms":0,"last_activity_at":null,"reminder_count":0,"last_reminded_at":null,"phone_number":null,"bankid_required":false,"personal_number_masked":null}],"active_send":{"id":"01a15a31-0c1f-7b90-a2e4-3d7c9b5f8e16","status":"sent","signing_order":"parallel","sent_at":"2026-10-12T08:14:03+00:00","expires_at":"2026-11-11T22:59:59+00:00"},"tags":[{"id":"01a14f88-2b1a-7c3e-8d4f-9e0a1b2c3d4e","slug":"ramavtal","label":"Ramavtal","color":"brand"}],"engagement":{"views":2,"unique_readers":1,"unique_sessions":2,"total_time_ms":412000,"first_opened_at":"2026-10-13T06:48:02+00:00","last_activity_at":"2026-10-13T06:51:19+00:00","event_counts":{"document.opened":2,"page.viewed":9,"time.spent":9}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateDocument","tags":["Documents"],"summary":"Update a document","description":"Changes the document's details. Send only the fields to change.\n\n`expires_at` is the document's own deadline: once it passes, an hourly sweep sets `lifecycle_status` to `expired` and expires its open signature request, after which the signing links stop working. Changing it here does not change the deadline of a signature request that was already sent — `expires_at` on `POST /documents/{document}/send` sets both.\n\n`tag_ids` replaces the document's tags; ids that are not tags of this organization are ignored.","x-scope":"write","x-module":null,"x-permissions":["documents.update"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":255},"value_cents":{"type":["integer","null"],"minimum":0,"maximum":99999999999999,"description":"What the document is worth, in the currency's minor unit (öre for SEK)."},"currency":{"type":"string","pattern":"^[A-Za-z]{3}$","description":"ISO 4217 code; stored upper-case."},"expires_at":{"type":["string","null"],"format":"date-time"},"signer_otp_required":{"type":"boolean","description":"Whether signers must enter a one-time code before they can sign or decline. Applies to recipients who have not signed yet."},"signer_verification_channel":{"type":"string","enum":["email","sms"],"description":"Where the code goes. `sms` needs a `phone_number` on the recipient and an SMS provider on the server; without either the code is emailed and the signing page says why."},"bankid_sign_required":{"type":"boolean","description":"Signers sign with BankID. Switching it on answers `402` on a tier without BankID and `503` (`bankid_unavailable`) when BankID is not configured on the server."},"bankid_identification_required":{"type":"boolean","description":"Recipients identify with BankID before the document is shown to them. Same `402`/`503` as `bankid_sign_required`."},"tag_ids":{"type":"array","maxItems":20,"items":{"type":"string","format":"uuid"},"description":"The complete new set of tags. `[]` removes them all."}},"additionalProperties":false},"example":{"title":"Serviceavtal 2027 – Byggbolaget i Mälardalen AB (rev. 2)","value_cents":18500000,"currency":"SEK","expires_at":"2026-11-30T22:59:59+00:00","tag_ids":["01a14f88-2b1a-7c3e-8d4f-9e0a1b2c3d4e"]}}}},"responses":{"200":{"description":"The updated document, with the same detail as `GET /documents/{document}`.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Document"}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/BankIdUnavailable"}}},"delete":{"operationId":"deleteDocument","tags":["Documents"],"summary":"Delete a document","description":"Deletes the document: it leaves the organization's lists and its id answers `404` from then on. Its signature requests and their audit trail are kept. To withdraw a document that is out for signing, cancel its signature request first (`POST /signatures/{signature}/cancel`, with the id from `active_send`) so the recipients' links stop working. Deleting cannot be undone through the API.","x-scope":"write","x-module":null,"x-permissions":["documents.delete"],"responses":{"200":{"description":"Deleted.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"boolean","enum":[true]}},"additionalProperties":false},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/documents/{document}/download-link":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"get":{"operationId":"getDocumentDownloadLink","tags":["Documents"],"summary":"Get a download link","description":"Returns a signed URL that downloads the document's file. The file is not streamed through the API itself.\n\n- The URL works for **15 minutes** (`expires_in_seconds`). Ask for a new one each time you download rather than storing it.\n- Fetch it with a plain `GET` and **without** the `Authorization` header: the signature in the query string is the credential, so treat the URL like a password — do not log it or put it in front of anyone who should not have the file.\n- The response is the file itself, with its `Content-Type` and the document's title as the file name. An expired or altered URL answers `403`.\n\n**What you download is the file as uploaded.** Closepact does not rewrite the PDF when it is signed: the signatures, with time, IP address and signature type, are recorded in the signature request's tamper-evident audit trail — `GET /signatures/{signature}` → `events`, each event hash-chained to the one before. Keep that alongside the file as the evidence of signing.\n\nThe member the key acts as needs `documents.read.any`.","x-scope":"read","x-module":null,"x-permissions":["documents.read.any"],"responses":{"200":{"description":"A short-lived download URL.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/DocumentDownloadLink"}},"additionalProperties":false},"example":{"data":{"url":"https://api.closepact.com/v1/documents/01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47/download?expires=1792310400&signature=3b9f6c1e0d2a7f4b8c5e9d1a6f3b0c7e2d8a5f1b9c4e7d0a3f6b2c8e5d1a9f4b","expires_in_seconds":900}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/documents/{document}/timeline":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"get":{"operationId":"getDocumentTimeline","tags":["Documents"],"summary":"Get a document's history","description":"Everything that happened to the document, newest first: what recipients did (`source: engagement` — `document.opened`, `page.viewed`, `time.spent`, `document.download`) merged with the signature audit trail (`source: signature` — `signature.requested`, `recipient.added`, `recipient.removed`, `signer.opened`, `signer.code_sent`, `signer.verified`, `signer.signed`, `signer.declined`, `signature.refused`, `reminder.sent`, `signature.completed`, `signature.sealed`, `signature.canceled`). At most the 200 most recent events.\n\n`by_recipient` summarises reading per recipient.\n\n`integrity` is the audit chain of every signature request the document was sent in, recomputed on this request: `ok` when every chain verifies, `broken` when any does (see each request's `broken_at`), `empty` when the document was never sent. The chain itself is on `GET /signatures/{signature}`.","x-scope":"read","x-module":null,"x-permissions":[],"responses":{"200":{"description":"The document's history.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/DocumentTimeline"}},"additionalProperties":false},"example":{"data":{"events":[{"id":"01a15b07-6e21-7c4a-9b3d-0f8e2a5c1d76","source":"engagement","event_type":"document.opened","actor":{"type":"recipient","id":"01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03","name":"Erik Johansson"},"page_number":null,"value":[],"occurred_at":"2026-10-13T06:48:02+00:00"},{"id":"01a15a31-0c30-7d18-8e2f-4a6c9b1d3e57","source":"signature","event_type":"signature.requested","actor":{"type":"user","id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist"},"page_number":null,"value":{"document_id":"01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47","signer_count":2},"occurred_at":"2026-10-12T08:14:03+00:00"}],"by_recipient":[{"signer_id":"01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03","name":"Erik Johansson","event_count":20,"first_seen":"2026-10-13T06:48:02+00:00","last_seen":"2026-10-13T06:51:19+00:00"}],"integrity":{"status":"ok","checked_at":"2026-10-13T07:02:11+00:00","requests":[{"signature_request_id":"01a15a31-0c1f-7b90-a2e4-3d7c9b5f8e16","status":"ok","format":"current","event_count":2,"head":"1d0e7c4b9a2f5e8d3c6b1a0f9e4d7c2b5a8f3e6d1c4b7a0e9f2d5c8b1a4e7f3d","broken_at":null}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/documents/{document}/engagement":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"get":{"operationId":"getDocumentEngagement","tags":["Documents"],"summary":"Get reading statistics","description":"How the document has been read: a summary, time and views per page (from page 1 to the last page, gaps included) and the same per recipient. Page statistics need page images, so they are only filled in for PDFs.","x-scope":"read","x-module":null,"x-permissions":[],"responses":{"200":{"description":"Reading statistics.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/DocumentEngagement"}},"additionalProperties":false},"example":{"data":{"summary":{"views":2,"unique_readers":1,"unique_sessions":2,"total_time_ms":412000,"first_opened_at":"2026-10-13T06:48:02+00:00","last_activity_at":"2026-10-13T06:51:19+00:00","event_counts":{"document.opened":2,"page.viewed":9,"time.spent":9}},"pages":[{"page_number":1,"duration_ms":38000,"views":2},{"page_number":2,"duration_ms":0,"views":0},{"page_number":3,"duration_ms":214000,"views":3}],"recipients":[{"recipient_id":"01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03","name":"Erik Johansson","email":"erik.johansson@byggbolaget.se","role":"signer","status":"opened","views":2,"total_time_ms":412000,"last_activity_at":"2026-10-13T06:51:19+00:00"}],"event_counts":{"document.opened":2,"page.viewed":9,"time.spent":9}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/documents/{document}/recipients":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"get":{"operationId":"listDocumentRecipients","tags":["Documents"],"summary":"List recipients","description":"Everyone the document has been sent to, in signing order — including recipients of earlier signature requests that a later send replaced. Not paginated.","x-scope":"read","x-module":null,"x-permissions":[],"responses":{"200":{"description":"The recipients.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DocumentRecipient"}}},"additionalProperties":false},"example":{"data":[{"id":"01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03","name":"Erik Johansson","email":"erik.johansson@byggbolaget.se","role":"signer","status":"signed","order_index":0,"opened_at":"2026-10-13T06:48:02+00:00","signed_at":"2026-10-13T06:52:44+00:00","declined_at":null,"decline_reason":null,"view_count":2,"total_time_ms":412000,"last_activity_at":"2026-10-13T06:52:44+00:00","reminder_count":1,"last_reminded_at":"2026-10-12T15:00:00+00:00","phone_number":null,"bankid_required":false,"personal_number_masked":null}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"addDocumentRecipient","tags":["Documents"],"summary":"Add a recipient","description":"Adds one recipient.\n\n**On a document that has not been sent, this sends it.** The first recipient creates the signature request (the `signature.requested` webhook fires), gets an email with their link straight away (whatever `send_invite` says), and the document moves to `sent`. To send to several people at once, use `POST /documents/{document}/send` instead.\n\nOn a document that has been sent, the recipient joins the current signature request — by default after the last recipient (`order_index`) — and is emailed their link unless `send_invite` is `false`.\n\n`role`: `signer` and `approver` must sign before the request completes; `cc` and `viewer` get a read-only link and never hold it open.\n\nSet `include_link` to get the recipient's private link back, for example to deliver it through your own channel. Treat it like a password: it opens the document as that person, without logging in.","x-scope":"write","x-module":null,"x-permissions":["documents.update"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","name"],"properties":{"email":{"type":"string","format":"email","maxLength":255},"name":{"type":"string","maxLength":255},"role":{"type":"string","enum":["signer","approver","cc","viewer"],"default":"signer"},"order_index":{"type":"integer","minimum":0,"maximum":100,"description":"Position in a sequential signing order. Defaults to after the last recipient. Ignored for the first recipient."},"message":{"type":["string","null"],"maxLength":2000,"description":"A personal note in the invitation email."},"send_invite":{"type":"boolean","default":true,"description":"Email the recipient their link. Only applies once the document has been sent; the first recipient is always emailed."},"phone_number":{"type":["string","null"],"maxLength":32,"description":"For the one-time code by SMS. International form (`+46 70 123 45 67`) or a Swedish number starting with 0; stored as E.164."},"bankid_required":{"type":"boolean","default":false,"description":"This recipient signs with BankID even when the document does not ask it of everybody."},"personal_number":{"type":["string","null"],"description":"The Swedish personal identity number (personnummer, 10 or 12 digits) BankID has to report for this recipient; anybody else's BankID is refused. Implies `bankid_required`. Stored encrypted and never returned; responses show `personal_number_masked`.","example":"19811218-9876"},"include_link":{"type":"boolean","default":false,"description":"Return the recipient's private link in `link`. May also be sent as a query parameter."}},"additionalProperties":false},"example":{"email":"maria.svensson@byggbolaget.se","name":"Maria Svensson","role":"approver","message":"Hej Maria! Här är avtalet för godkännande.","include_link":true}}}},"responses":{"201":{"description":"The recipient, and their link when asked for.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","link"],"properties":{"data":{"$ref":"#/components/schemas/DocumentRecipient"},"link":{"type":["object","null"],"description":"Only when `include_link` was set; otherwise null.","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"The recipient's private signing (or viewing) link."}},"additionalProperties":false}},"additionalProperties":false},"example":{"data":{"id":"01a15a31-0c2e-71aa-9c3d-6b1e8f2a4d59","name":"Maria Svensson","email":"maria.svensson@byggbolaget.se","role":"approver","status":"pending","order_index":1,"opened_at":null,"signed_at":null,"declined_at":null,"decline_reason":null,"view_count":0,"total_time_ms":0,"last_activity_at":null,"reminder_count":0,"last_reminded_at":null,"phone_number":null,"bankid_required":false,"personal_number_masked":null},"link":{"url":"https://app.closepact.com/sign/Xk3pQ9vB2mR7tY4wN8cL1zF6hJ0sD5gA2eK9uP3oW7iV4bM1"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/BankIdUnavailable"}}}},"/documents/{document}/recipients/{recipient}":{"parameters":[{"$ref":"#/components/parameters/DocumentId"},{"name":"recipient","in":"path","required":true,"description":"The recipient's id, from `recipients` on the document.","schema":{"type":"string","format":"uuid"}}],"delete":{"operationId":"removeDocumentRecipient","tags":["Documents"],"summary":"Remove a recipient","description":"Takes a recipient off the document. Their link stops working at once; nobody is emailed. The removal is recorded in the audit trail (`recipient.removed`).\n\nA recipient who has already signed cannot be removed — their signature is part of the audit trail — and the answer is `409`.","x-scope":"write","x-module":null,"x-permissions":["documents.update"],"responses":{"204":{"description":"Removed. No body.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/DocumentConflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/documents/{document}/send":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"post":{"operationId":"sendDocument","tags":["Documents"],"summary":"Send for signing","description":"Sends the document to its recipients — the main call for e-signing.\n\nCreates a signature request, emails every recipient their own private link, records `signature.requested` in the audit trail, fires the `signature.requested` webhook and moves the document to `sent`. Recipients sign in the browser, with a typed or drawn signature; no Closepact account is needed. When `signer_otp_required` is on (the default for new documents) they first enter a one-time code, by email or — with `signer_verification_channel` `sms` — by SMS. With `bankid_sign_required` (or `bankid_required` on a recipient) they sign with BankID instead, and with `bankid_identification_required` they identify with BankID before the document is shown. The SHA-256 of the file is taken now and recorded; if the file changes afterwards, signing is refused.\n\n**Sending again replaces the previous request.** Its links stop working and it is canceled (`Superseded by a new send.`, with a `signature.canceled` webhook), so the recipients you send now are the whole list. The document's `lifecycle_status` starts over at `sent` — whether the last round ended `opened`, `expired` or `canceled` — and `sent_at` keeps the first send. A request that has already been completed is never voided, and a `signed` document stays `signed`.\n\n**Signing order.** `parallel` (default): everyone may sign at once. `sequential`: recipients sign in the order listed; a recipient cannot sign before everyone earlier in the list who must sign has done so.\n\n**What happens next.** When a recipient opens their link the `document.viewed` webhook fires and `lifecycle_status` moves to `opened`. Each signature fires `signer.signed`. When every `signer` and `approver` has signed, the request is `completed`, the document `signed`, and the `signature.completed` webhook fires with the signature request's `id` and the `document_id`; a completion certificate is issued and every recipient and the sender are emailed links to the signed document and the certificate. A recipient who declines must give a reason; the request becomes `declined`, the document `declined`, and the sender is emailed. If `expires_at` passes first, the request and the document become `expired`. Unsigned recipients can be nudged with `POST /documents/{document}/remind`.\n\nSet `include_links` to get each recipient's private link back.","x-scope":"write","x-module":null,"x-permissions":["documents.send"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["recipients"],"properties":{"recipients":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"object","required":["email","name"],"properties":{"email":{"type":"string","format":"email","maxLength":255},"name":{"type":"string","maxLength":255},"role":{"type":"string","enum":["signer","approver","cc","viewer"],"default":"signer","description":"`signer` and `approver` must sign; `cc` and `viewer` receive a read-only link."},"phone_number":{"type":["string","null"],"maxLength":32,"description":"For the one-time code by SMS. International form (`+46 70 123 45 67`) or a Swedish number starting with 0; stored as E.164."},"bankid_required":{"type":"boolean","default":false,"description":"This recipient signs with BankID even when the document does not ask it of everybody."},"personal_number":{"type":["string","null"],"description":"The Swedish personal identity number (personnummer, 10 or 12 digits) BankID has to report for this recipient; anybody else's BankID is refused. Implies `bankid_required`. Stored encrypted and never returned; responses show `personal_number_masked`.","example":"19811218-9876"}},"additionalProperties":false}},"title":{"type":"string","maxLength":255,"description":"Subject of the signature request. Defaults to the document's title."},"message":{"type":["string","null"],"maxLength":5000,"description":"A personal note in every recipient's email."},"signing_order":{"type":"string","enum":["parallel","sequential"],"default":"parallel"},"expires_at":{"type":["string","null"],"format":"date-time","description":"Signing deadline; must be in the future. Also becomes the document's `expires_at`. Defaults to the document's `expires_at`."},"deal_room_id":{"type":["string","null"],"format":"uuid","description":"Link the signature request to a deal room of this organization. Any other id, including another organization's room, fails validation (`422`)."},"include_links":{"type":"boolean","default":false,"description":"Return each recipient's private link in `links`. May also be sent as a query parameter."}},"additionalProperties":false},"example":{"recipients":[{"email":"erik.johansson@byggbolaget.se","name":"Erik Johansson","role":"signer"},{"email":"maria.svensson@byggbolaget.se","name":"Maria Svensson","role":"cc"}],"message":"Hej Erik! Här kommer serviceavtalet för 2027 enligt vårt samtal. Hör av dig om något är oklart.","signing_order":"parallel","expires_at":"2026-11-11T22:59:59+00:00"}}}},"responses":{"201":{"description":"Sent. The document with its recipients and the new signature request in `active_send`.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links"],"properties":{"data":{"$ref":"#/components/schemas/Document"},"links":{"type":["array","null"],"description":"Only when `include_links` was set; otherwise null. One entry per recipient of this send.","items":{"type":"object","required":["recipient_id","email","url"],"properties":{"recipient_id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"url":{"type":"string","format":"uri","description":"The recipient's private signing (or viewing) link."}},"additionalProperties":false}}},"additionalProperties":false},"example":{"data":{"id":"01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47","title":"Serviceavtal 2027 – Byggbolaget i Mälardalen AB","type":"pdf","mime_type":"application/pdf","size_bytes":284516,"page_count":6,"page_images_status":"ready","status":"ready","lifecycle_status":"sent","value_cents":18500000,"currency":"SEK","sent_at":"2026-10-12T08:14:03+00:00","expires_at":"2026-11-11T22:59:59+00:00","days_left":30,"checksum_sha256":"9f2c4b7e1d0a8c3f5e6b2d9a7c4e1f0b3d8a6c2e5f9b1d7a4c0e3f6b8d2a5c9e","signer_otp_required":true,"signer_verification_channel":"email","bankid_sign_required":false,"bankid_identification_required":false,"metadata":{"original_name":"Serviceavtal_2027_Byggbolaget.pdf","has_text":true,"text_length":18234,"processed_at":"2026-10-12T08:12:41+00:00"},"created_at":"2026-10-12T08:12:37+00:00","updated_at":"2026-10-12T08:14:03+00:00","recipients":[{"id":"01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03","name":"Erik Johansson","email":"erik.johansson@byggbolaget.se","role":"signer","status":"pending","order_index":0,"opened_at":null,"signed_at":null,"declined_at":null,"decline_reason":null,"view_count":0,"total_time_ms":0,"last_activity_at":null,"reminder_count":0,"last_reminded_at":null,"phone_number":null,"bankid_required":false,"personal_number_masked":null}],"active_send":{"id":"01a15a31-0c1f-7b90-a2e4-3d7c9b5f8e16","status":"sent","signing_order":"parallel","sent_at":"2026-10-12T08:14:03+00:00","expires_at":"2026-11-11T22:59:59+00:00"}},"links":null}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/BankIdUnavailable"}}}},"/documents/{document}/remind":{"parameters":[{"$ref":"#/components/parameters/DocumentId"}],"post":{"operationId":"remindDocumentRecipients","tags":["Documents"],"summary":"Remind recipients","description":"Emails a reminder, with their existing link, to the recipients of the current signature request who have not finished — or only to `recipient_id`.\n\nRecipients are skipped, and listed in `skipped` with the reason, when they have already signed (`already_signed`) or declined (`declined`), were reminded less than 60 minutes ago (`cooldown`), or the document has expired (`document_expired`). Each reminder is recorded in the audit trail (`reminder.sent`) and counted in the recipient's `reminder_count`.\n\nAnswers `409` when the document has not been sent yet, or `recipient_id` is not on its current signature request.","x-scope":"write","x-module":null,"x-permissions":["documents.send"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"recipient_id":{"type":["string","null"],"format":"uuid","description":"Remind only this recipient. Omit to remind everyone who has not finished."},"message":{"type":["string","null"],"maxLength":2000,"description":"A personal note in the reminder email."}},"additionalProperties":false},"example":{"message":"Hej! En påminnelse om serviceavtalet — det går ut den 11 november."}}}},"responses":{"200":{"description":"How many reminders went out, and who was skipped.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/DocumentReminderResult"}},"additionalProperties":false},"example":{"data":{"sent":1,"skipped":[{"recipient_id":"01a15a31-0c2e-71aa-9c3d-6b1e8f2a4d59","reason":"already_signed"}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/DocumentConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/employees":{"get":{"operationId":"listEmployees","tags":["Employees"],"summary":"List employees","description":"Employees by last name, then first name, with their skills and linked login. For an HR or payroll sync, match on `employee_number`. Cursor-paginated: follow `links.next`, or pass `meta.next_cursor` as `cursor`.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"q","in":"query","required":false,"description":"Search in first name, last name and employee number (case-insensitive, substring).","schema":{"type":"string"},"example":"Hassan"},{"name":"filter[status]","in":"query","required":false,"description":"Only `active` or only `inactive` employees.","schema":{"type":"string","enum":["active","inactive"]}},{"name":"filter[skill_id]","in":"query","required":false,"description":"Only employees holding this skill.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1–100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of employees.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Employee"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","employee_number":"A-017","first_name":"Ali","last_name":"Hassan","name":"Ali Hassan","email":"ali.hassan@nordiskfastighet.se","phone":"073-981 22 40","employment_type":"employed","weekly_hours":40,"hourly_cost_cents":32000,"hourly_billing_rate_cents":65000,"color":"#2F80ED","status":"active","started_at":"2024-03-01","ended_at":null,"user":{"id":"01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091","name":"Ali Hassan","email":"ali.hassan@nordiskfastighet.se"},"skills":[{"id":"01a15b70-7c8d-7e9f-a0b1-c2d3e4f50617","name":"Storstädning","color":"#27AE60","level":3}],"created_at":"2024-02-20T08:30:00+00:00","updated_at":"2026-10-01T12:00:00+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":null},"meta":{"path":"https://api.closepact.com/v1/api/employees","per_page":25,"next_cursor":null,"prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createEmployee","tags":["Employees"],"summary":"Create an employee","description":"Adds somebody who can be scheduled — from an HR system, say. An employee does not need a login and does not use a licence; link one with `user_id` if they should see their own jobs in the field app.","x-scope":"write","x-module":"booking","x-permissions":["booking.resources.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["first_name","last_name"],"properties":{"first_name":{"type":"string","description":"First name.","maxLength":128},"last_name":{"type":"string","description":"Last name.","maxLength":128},"employee_number":{"type":["string","null"],"description":"Your own number for them, e.g. the payroll id.","maxLength":32},"email":{"type":["string","null"],"format":"email","maxLength":320,"description":"Email address for assignment notices."},"phone":{"type":["string","null"],"description":"Phone number.","maxLength":32},"employment_type":{"type":"string","enum":["employed","hourly","contractor"],"description":"Default `employed`."},"hourly_cost_cents":{"type":["integer","null"],"description":"Cost of an hour of this person in öre — wage and overheads, never invoiced.","minimum":0},"hourly_billing_rate_cents":{"type":["integer","null"],"description":"What an hour of this person is invoiced at, in öre excluding VAT. An hourly job bills this person's approved time at it when the job was not quoted one service price per hour.","minimum":0},"weekly_hours":{"type":["number","null"],"description":"Contracted hours per week, 0–168.","minimum":0,"maximum":168},"color":{"type":["string","null"],"description":"Colour on the board, e.g. `#2F80ED`.","maxLength":16},"status":{"type":"string","enum":["active","inactive"],"description":"Default `active`."},"started_at":{"type":["string","null"],"format":"date","description":"First day of employment. Calendar date, `YYYY-MM-DD`."},"ended_at":{"type":["string","null"],"format":"date","description":"Last day of employment; not before `started_at`. Calendar date, `YYYY-MM-DD`."},"user_id":{"type":["string","null"],"format":"uuid","description":"Link to the login of a member of your organization (an active member), or null for none."},"sub_entity_id":{"type":["string","null"],"format":"uuid","description":"The office (sub-entity) the employee belongs to. Must be one of yours."},"skill_ids":{"type":"array","description":"Replaces the employee's skills with these (see `GET /skills`). Ids that are not your organization's are ignored. Leave out to keep the current skills; `[]` removes them all. A skill the employee already holds keeps its level; a new one starts at 1. To set levels, send `skills` instead.","items":{"type":"string","format":"uuid"}},"skills":{"type":"array","description":"Replaces the employee's skills with these, each with a level. Same rules as `skill_ids`, which it cannot be sent together with. A skill sent without `level` is level 1.","items":{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"uuid","description":"The skill id (see `GET /skills`)."},"level":{"type":"integer","minimum":1,"maximum":5,"description":"How good they are at it, 1–5. Default 1."}},"additionalProperties":false}}},"additionalProperties":false},"example":{"first_name":"Ali","last_name":"Hassan","employee_number":"A-017","email":"ali.hassan@nordiskfastighet.se","phone":"073-981 22 40","employment_type":"employed","hourly_cost_cents":32000,"hourly_billing_rate_cents":65000,"weekly_hours":40,"started_at":"2024-03-01","skill_ids":["01a15b70-7c8d-7e9f-a0b1-c2d3e4f50617"]}}}},"responses":{"201":{"description":"The employee, with skills (without `user`).","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Employee"}},"additionalProperties":false},"example":{"data":{"id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","employee_number":"A-017","first_name":"Ali","last_name":"Hassan","name":"Ali Hassan","email":"ali.hassan@nordiskfastighet.se","phone":"073-981 22 40","employment_type":"employed","weekly_hours":40,"hourly_cost_cents":32000,"hourly_billing_rate_cents":65000,"color":"#2F80ED","status":"active","started_at":"2024-03-01","ended_at":null,"skills":[{"id":"01a15b70-7c8d-7e9f-a0b1-c2d3e4f50617","name":"Storstädning","color":"#27AE60","level":3}],"created_at":"2024-02-20T08:30:00+00:00","updated_at":"2026-10-01T12:00:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/employees/{employee}":{"get":{"operationId":"getEmployee","tags":["Employees"],"summary":"Get an employee","description":"One employee with skills, linked login and weekly availability. A deleted employee, or another organization's, is a 404.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"employee","in":"path","required":true,"description":"The employee id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The employee.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Employee"}},"additionalProperties":false},"example":{"data":{"id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","employee_number":"A-017","first_name":"Ali","last_name":"Hassan","name":"Ali Hassan","email":"ali.hassan@nordiskfastighet.se","phone":"073-981 22 40","employment_type":"employed","weekly_hours":40,"hourly_cost_cents":32000,"hourly_billing_rate_cents":65000,"color":"#2F80ED","status":"active","started_at":"2024-03-01","ended_at":null,"user":{"id":"01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091","name":"Ali Hassan","email":"ali.hassan@nordiskfastighet.se"},"skills":[{"id":"01a15b70-7c8d-7e9f-a0b1-c2d3e4f50617","name":"Storstädning","color":"#27AE60","level":3}],"availability":[{"id":"01a15b91-2a3b-7c4d-8e5f-60718293a4b5","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","weekday":1,"start_time":"07:00:00","end_time":"11:30:00","valid_from":"2026-01-01T00:00:00.000000Z","valid_to":null,"created_at":"2025-12-18T10:00:00.000000Z","updated_at":"2025-12-18T10:00:00.000000Z"},{"id":"01a15b91-2a3c-7d5e-9f60-718293a4b5c6","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","weekday":1,"start_time":"12:15:00","end_time":"16:00:00","valid_from":"2026-01-01T00:00:00.000000Z","valid_to":null,"created_at":"2025-12-18T10:00:00.000000Z","updated_at":"2025-12-18T10:00:00.000000Z"}],"created_at":"2024-02-20T08:30:00+00:00","updated_at":"2026-10-01T12:00:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateEmployee","tags":["Employees"],"summary":"Update an employee","description":"Changes the fields you send. To take somebody off the schedule but keep their history, set `status` to `inactive` (or `ended_at`) rather than deleting them.","x-scope":"write","x-module":"booking","x-permissions":["booking.resources.manage"],"parameters":[{"name":"employee","in":"path","required":true,"description":"The employee id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","description":"First name.","maxLength":128},"last_name":{"type":"string","description":"Last name.","maxLength":128},"employee_number":{"type":["string","null"],"description":"Your own number for them, e.g. the payroll id.","maxLength":32},"email":{"type":["string","null"],"format":"email","maxLength":320,"description":"Email address for assignment notices."},"phone":{"type":["string","null"],"description":"Phone number.","maxLength":32},"employment_type":{"type":"string","enum":["employed","hourly","contractor"],"description":"Default `employed`."},"hourly_cost_cents":{"type":["integer","null"],"description":"Cost of an hour of this person in öre — wage and overheads, never invoiced.","minimum":0},"hourly_billing_rate_cents":{"type":["integer","null"],"description":"What an hour of this person is invoiced at, in öre excluding VAT. An hourly job bills this person's approved time at it when the job was not quoted one service price per hour.","minimum":0},"weekly_hours":{"type":["number","null"],"description":"Contracted hours per week, 0–168.","minimum":0,"maximum":168},"color":{"type":["string","null"],"description":"Colour on the board, e.g. `#2F80ED`.","maxLength":16},"status":{"type":"string","enum":["active","inactive"],"description":"Default `active`."},"started_at":{"type":["string","null"],"format":"date","description":"First day of employment. Calendar date, `YYYY-MM-DD`."},"ended_at":{"type":["string","null"],"format":"date","description":"Last day of employment; not before `started_at`. Calendar date, `YYYY-MM-DD`."},"user_id":{"type":["string","null"],"format":"uuid","description":"Link to the login of a member of your organization (an active member), or null for none."},"sub_entity_id":{"type":["string","null"],"format":"uuid","description":"The office (sub-entity) the employee belongs to. Must be one of yours."},"skill_ids":{"type":"array","description":"Replaces the employee's skills with these (see `GET /skills`). Ids that are not your organization's are ignored. Leave out to keep the current skills; `[]` removes them all. A skill the employee already holds keeps its level; a new one starts at 1. To set levels, send `skills` instead.","items":{"type":"string","format":"uuid"}},"skills":{"type":"array","description":"Replaces the employee's skills with these, each with a level. Same rules as `skill_ids`, which it cannot be sent together with. A skill sent without `level` is level 1.","items":{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"uuid","description":"The skill id (see `GET /skills`)."},"level":{"type":"integer","minimum":1,"maximum":5,"description":"How good they are at it, 1–5. Default 1."}},"additionalProperties":false}}},"additionalProperties":false},"example":{"phone":"070-123 45 67","weekly_hours":32}}}},"responses":{"200":{"description":"The employee, with skills (without `user`).","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Employee"}},"additionalProperties":false},"example":{"data":{"id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","employee_number":"A-017","first_name":"Ali","last_name":"Hassan","name":"Ali Hassan","email":"ali.hassan@nordiskfastighet.se","phone":"070-123 45 67","employment_type":"employed","weekly_hours":32,"hourly_cost_cents":32000,"hourly_billing_rate_cents":65000,"color":"#2F80ED","status":"active","started_at":"2024-03-01","ended_at":null,"skills":[{"id":"01a15b70-7c8d-7e9f-a0b1-c2d3e4f50617","name":"Storstädning","color":"#27AE60","level":3}],"created_at":"2024-02-20T08:30:00+00:00","updated_at":"2026-10-01T12:00:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteEmployee","tags":["Employees"],"summary":"Delete an employee","description":"Removes the employee from lists and reads. Their name stays on past jobs and their hours on past timesheets. For somebody who has left, `status: inactive` is usually what you want.","x-scope":"write","x-module":"booking","x-permissions":["booking.resources.manage"],"parameters":[{"name":"employee","in":"path","required":true,"description":"The employee id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The employee was deleted.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingDeleted"},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/employees/{employee}/availability":{"get":{"operationId":"listAvailabilityRules","tags":["Employees"],"summary":"List an employee's working hours","description":"All of the employee's availability rules, by weekday and start time — the whole set, not a page. Rules that have expired (`valid_to` in the past) are included.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"employee","in":"path","required":true,"description":"The employee id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The rules.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityRule"}}},"additionalProperties":false},"example":{"data":[{"id":"01a15b91-2a3b-7c4d-8e5f-60718293a4b5","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","weekday":1,"start_time":"07:00:00","end_time":"11:30:00","valid_from":"2026-01-01T00:00:00.000000Z","valid_to":null,"created_at":"2025-12-18T10:00:00.000000Z","updated_at":"2025-12-18T10:00:00.000000Z"},{"id":"01a15b91-2a3c-7d5e-9f60-718293a4b5c6","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","weekday":1,"start_time":"12:15:00","end_time":"16:00:00","valid_from":"2026-01-01T00:00:00.000000Z","valid_to":null,"created_at":"2025-12-18T10:00:00.000000Z","updated_at":"2025-12-18T10:00:00.000000Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createAvailabilityRule","tags":["Employees"],"summary":"Add working hours","description":"Adds the hours an employee normally works on one weekday. The schedule warns (`outside_working_hours`) when a job falls outside them; it never refuses. A split shift is two rules on the same day; gaps of up to an hour between them count as one working day.\n\nTo change hours from a date, add a rule with `valid_from` and end the old one with `valid_to`, so earlier timesheets keep the hours they were planned against. An identical rule (same day, times and dates) is a 409; an end before the start is a 422.","x-scope":"write","x-module":"booking","x-permissions":["booking.resources.manage"],"parameters":[{"name":"employee","in":"path","required":true,"description":"The employee id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["weekday","start_time","end_time"],"properties":{"weekday":{"type":"integer","description":"ISO weekday: 1 = Monday … 7 = Sunday.","minimum":1,"maximum":7},"start_time":{"type":"string","pattern":"^\\d{2}:\\d{2}(:\\d{2})?$","description":"Start, `HH:MM` (or `HH:MM:SS`; seconds are dropped), in Swedish local time (`Europe/Stockholm`). Job times are converted to that clock before they are compared, so `07:00` means seven in the morning in Sweden, summer and winter."},"end_time":{"type":"string","pattern":"^\\d{2}:\\d{2}(:\\d{2})?$","description":"End, after the start (otherwise 422)."},"valid_from":{"type":["string","null"],"format":"date","description":"First day the rule applies, or null for always. Calendar date, `YYYY-MM-DD`."},"valid_to":{"type":["string","null"],"format":"date","description":"Last day the rule applies, or null for open-ended; not before `valid_from`. Calendar date, `YYYY-MM-DD`."}},"additionalProperties":false},"example":{"weekday":1,"start_time":"07:00","end_time":"11:30","valid_from":"2026-01-01"}}}},"responses":{"201":{"description":"The new rule.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AvailabilityRule"}},"additionalProperties":false},"example":{"data":{"id":"01a15b91-2a3b-7c4d-8e5f-60718293a4b5","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","weekday":1,"start_time":"07:00:00","end_time":"11:30:00","valid_from":"2026-01-01T00:00:00.000000Z","valid_to":null,"created_at":"2025-12-18T10:00:00.000000Z","updated_at":"2025-12-18T10:00:00.000000Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The employee already has exactly this rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConflictError"},"example":{"message":"Those hours are already on that day.","error":"conflict"}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/employees/{employee}/availability/{rule}":{"patch":{"operationId":"updateAvailabilityRule","tags":["Employees"],"summary":"Change working hours","description":"Changes the fields you send; the result is checked as a whole (sending only `end_time` still has to end after the stored start). A rule of another employee is a 404.","x-scope":"write","x-module":"booking","x-permissions":["booking.resources.manage"],"parameters":[{"name":"employee","in":"path","required":true,"description":"The employee id.","schema":{"type":"string","format":"uuid"}},{"name":"rule","in":"path","required":true,"description":"The availability rule id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"weekday":{"type":"integer","description":"ISO weekday: 1 = Monday … 7 = Sunday.","minimum":1,"maximum":7},"start_time":{"type":"string","pattern":"^\\d{2}:\\d{2}(:\\d{2})?$","description":"Start, `HH:MM` (or `HH:MM:SS`; seconds are dropped), in Swedish local time (`Europe/Stockholm`). Job times are converted to that clock before they are compared, so `07:00` means seven in the morning in Sweden, summer and winter."},"end_time":{"type":"string","pattern":"^\\d{2}:\\d{2}(:\\d{2})?$","description":"End, after the start (otherwise 422)."},"valid_from":{"type":["string","null"],"format":"date","description":"First day the rule applies, or null for always. Calendar date, `YYYY-MM-DD`."},"valid_to":{"type":["string","null"],"format":"date","description":"Last day the rule applies, or null for open-ended; not before `valid_from`. Calendar date, `YYYY-MM-DD`."}},"additionalProperties":false},"example":{"end_time":"11:00"}}}},"responses":{"200":{"description":"The rule.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AvailabilityRule"}},"additionalProperties":false},"example":{"data":{"id":"01a15b91-2a3b-7c4d-8e5f-60718293a4b5","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","weekday":1,"start_time":"07:00:00","end_time":"11:00:00","valid_from":"2026-01-01T00:00:00.000000Z","valid_to":null,"created_at":"2025-12-18T10:00:00.000000Z","updated_at":"2025-12-18T10:00:00.000000Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The change would duplicate another of the employee's rules.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConflictError"},"example":{"message":"Those hours are already on that day.","error":"conflict"}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteAvailabilityRule","tags":["Employees"],"summary":"Remove working hours","description":"Deletes the rule. An employee left with no rules is treated as always available.","x-scope":"write","x-module":"booking","x-permissions":["booking.resources.manage"],"parameters":[{"name":"employee","in":"path","required":true,"description":"The employee id.","schema":{"type":"string","format":"uuid"}},{"name":"rule","in":"path","required":true,"description":"The availability rule id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The rule was deleted.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingDeleted"},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/skills":{"get":{"operationId":"listSkills","tags":["Employees"],"summary":"List skills","description":"The organization's competences, by name — the ids to use in `skill_ids` on employees and services, and in `filter[skill_id]`. The whole list, not a page. Skills are managed in the panel.","x-scope":"read","x-module":"booking","x-permissions":[],"responses":{"200":{"description":"The skills.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Skill"}}},"additionalProperties":false},"example":{"data":[{"id":"01a15b70-7c8e-7f00-b1c2-d3e4f5061728","name":"Fönsterputs","slug":"fonsterputs","color":"#56CCF2"},{"id":"01a15b70-7c8d-7e9f-a0b1-c2d3e4f50617","name":"Storstädning","slug":"storstadning","color":"#27AE60"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoices":{"get":{"operationId":"listInvoices","tags":["Invoices"],"summary":"List invoices","description":"Invoices and credit notes, newest first, without their lines (fetch one invoice for those).\n\nFor bookkeeping, `filter[from]`/`filter[to]` select on the **issue date** — the date an accountant means by \"September\" — and `filter[type]=credit_note` gives the credit notes. `filter[overdue]=true` is decided at the moment you ask (issued, due date passed, and something still owed by the customer once a ROT/RUT share is set aside), so it is more current than `filter[status]=overdue`, which a nightly job writes.\n\n`q` is a case-insensitive *contains* match on the invoice number and on *Er referens* / *Vår referens*.\n\nPaging is by cursor: pass `meta.next_cursor` back as `cursor` together with the same filters and `limit` — the URLs in `links` do not carry your filters.","x-scope":"read","x-module":"invoicing","x-permissions":[],"parameters":[{"name":"q","in":"query","required":false,"description":"Search the invoice number and the two references.","schema":{"type":"string"},"example":"1042"},{"name":"filter[status]","in":"query","required":false,"description":"One status, or several as `filter[status][]=sent&filter[status][]=partially_paid`.","schema":{"oneOf":[{"$ref":"#/components/schemas/InvoiceStatus"},{"type":"array","items":{"$ref":"#/components/schemas/InvoiceStatus"}}]}},{"name":"filter[account_id]","in":"query","required":false,"description":"Only this customer's invoices.","schema":{"type":"string","format":"uuid"}},{"name":"filter[type]","in":"query","required":false,"description":"Invoices, credit notes, or (repeated as `filter[type][]`) both.","schema":{"oneOf":[{"type":"string","enum":["invoice","credit_note"]},{"type":"array","items":{"type":"string","enum":["invoice","credit_note"]}}]}},{"name":"filter[overdue]","in":"query","required":false,"description":"`true` for invoices that are overdue right now.","schema":{"type":"boolean"}},{"name":"filter[from]","in":"query","required":false,"description":"Issued on or after this date.","schema":{"type":"string","format":"date"},"example":"2026-09-01"},{"name":"filter[to]","in":"query","required":false,"description":"Issued on or before this date.","schema":{"type":"string","format":"date"},"example":"2026-09-30"},{"name":"limit","in":"query","required":false,"description":"Invoices per page.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of invoices.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a15b02-7c14-7e2b-8d5a-3f9c1e6b4a27","type":"invoice","invoice_number":"1042","series":"A","reference":"A-1042","status":"sent","status_label":"Skickad","is_editable":false,"handover":null,"customer":{"id":"01a15a21-0c44-7b19-8e2d-6f3a9c1b5d70","name":"Eva Ek","customer_number":"1043","payment_terms_days":20},"dates":{"issue_date":"2026-10-18","due_date":"2026-11-07","delivery_date":"2026-10-16","payment_terms_days":20,"days_overdue":0},"references":{"your_reference":"Eva Ek","our_reference":"Anna Lindqvist","ocr_reference":"10421"},"amounts":{"currency":"SEK","net_cents":195000,"vat_cents":48750,"rounding_cents":50,"total_cents":243800,"paid_cents":0,"outstanding_cents":131300,"vat_by_rate":[{"rate":25,"net_cents":195000,"vat_cents":48750}]},"rot_rut":{"type":"rut","work_type":"Stadning","requested_cents":112500,"approved_cents":0,"refused_cents":0,"status":"none","customer_pays_cents":131300,"annual_ceiling_applied":false,"tax_profile_id":null,"buyer":null},"delivery_method":"email","delivery":null,"delivery_failure":null,"sent_at":"2026-10-18T09:20:44+00:00","viewed_at":null,"notes":null,"created_at":"2026-10-17T15:02:10+00:00","updated_at":"2026-10-18T09:20:44+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":null},"meta":{"path":"https://api.closepact.com/v1/api/invoices","per_page":50,"next_cursor":null,"prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createInvoice","tags":["Invoices"],"summary":"Create an invoice draft","description":"Creates a draft for a customer. Add its rows with `POST /invoices/{invoice}/lines`, then send it.\n\nOnly `account_id` is required. The rest comes from the customer: payment terms (then the organization's default, 30 days) and currency. `delivery_method` defaults to `email`.\n\n**The dates on a draft are provisional.** Sending stamps the invoice with the day it is sent, by the Swedish calendar, and a due date `payment_terms_days` after that — whatever `issue_date` and `due_date` the draft held. To give the customer 20 days, send `payment_terms_days: 20`. `delivery_date` (leveransdatum, when the work was done) is kept as you set it; it decides the VAT period.\n\n**ROT/RUT.** `rot_rut: \"rot\"` or `\"rut\"` makes Closepact add and maintain the deduction line from the eligible labour lines. Use it only for private customers, and make sure the customer has a tax profile — see the Invoices section.\n\nNo number, OCR reference or amount can be set here; they are produced by the lines and by sending. A customer, contact or office id from another organization answers 404.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.invoices.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/InvoiceFields"},{"type":"object","required":["account_id"]}]},"example":{"account_id":"01a15a21-0c44-7b19-8e2d-6f3a9c1b5d70","contact_id":"01a15a21-0c7a-70e4-9b3c-5d8a2f6e1c49","delivery_date":"2026-10-16","payment_terms_days":20,"your_reference":"Eva Ek","our_reference":"Anna Lindqvist","rot_rut":"rut"}}}},"responses":{"201":{"description":"The draft, with its lines (none yet).","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Invoice"}},"additionalProperties":false},"example":{"data":{"id":"01a15b02-7c14-7e2b-8d5a-3f9c1e6b4a27","type":"invoice","invoice_number":null,"series":"A","reference":"Utkast","status":"draft","status_label":"Utkast","is_editable":true,"handover":null,"customer":{"id":"01a15a21-0c44-7b19-8e2d-6f3a9c1b5d70","name":"Eva Ek","customer_number":"1043","payment_terms_days":20},"dates":{"issue_date":"2026-10-17","due_date":"2026-11-06","delivery_date":"2026-10-16","payment_terms_days":20,"days_overdue":0},"references":{"your_reference":"Eva Ek","our_reference":"Anna Lindqvist","ocr_reference":null},"amounts":{"currency":"SEK","net_cents":0,"vat_cents":0,"rounding_cents":0,"total_cents":0,"paid_cents":0,"outstanding_cents":0,"vat_by_rate":[]},"rot_rut":{"type":"rut","work_type":null,"requested_cents":0,"approved_cents":0,"refused_cents":0,"status":"none","customer_pays_cents":0,"annual_ceiling_applied":false,"tax_profile_id":null,"buyer":null},"lines":[],"delivery_method":"email","delivery":null,"delivery_failure":null,"sent_at":null,"viewed_at":null,"notes":null,"created_at":"2026-10-17T15:02:10+00:00","updated_at":"2026-10-17T15:02:10+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoices/from-work-orders":{"post":{"operationId":"createInvoiceFromWorkOrders","tags":["Invoices"],"summary":"Create a draft from finished work orders","description":"Builds one invoice draft from up to 100 finished work orders of the scheduling module, the way the panel's *Fakturera* button does, and marks the work orders invoiced.\n\nWhat is billed is what scheduling has settled: approved time only (hours still awaiting approval are left out and reported in `meta.warnings`), services and materials as separate lines, and the agreed price instead of the hours for a fixed-price job. The ROT/RUT type comes from the work orders.\n\nRefused with 409 when the work orders belong to different customers, disagree about the deduction, are not finished, or were already invoiced. A work order id the organization cannot see answers 404, and so does every request from an organization without the scheduling module. The result is an ordinary draft: check `meta.warnings`, adjust the lines if needed, and send it.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.invoices.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["work_order_ids"],"properties":{"work_order_ids":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string","format":"uuid"}},"issue_date":{"type":"string","format":"date","description":"Provisional, as on any draft."},"due_date":{"type":"string","format":"date","description":"On or after `issue_date`. Provisional, as on any draft."},"delivery_date":{"type":"string","format":"date"},"your_reference":{"type":["string","null"],"maxLength":128},"our_reference":{"type":["string","null"],"maxLength":128},"notes":{"type":["string","null"]}},"additionalProperties":false},"example":{"work_order_ids":["01a158e4-6b1d-7a29-8c5f-2e7d4b9a3c16"],"delivery_date":"2026-10-16","our_reference":"Anna Lindqvist"}}}},"responses":{"201":{"description":"The draft, its lines, and what the office should look at before sending it.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/Invoice"},"meta":{"type":"object","required":["warnings","work_order_ids"],"properties":{"warnings":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceSourceWarning"}},"work_order_ids":{"type":"array","items":{"type":"string","format":"uuid"}}},"additionalProperties":false}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/InvoiceConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoices/{invoice}":{"parameters":[{"$ref":"#/components/parameters/InvoiceId"}],"get":{"operationId":"getInvoice","tags":["Invoices"],"summary":"Get an invoice","description":"One invoice or credit note with its contact and all its lines, including the generated ROT/RUT line.\n\nWhat bookkeeping needs is here: `amounts.vat_by_rate` gives the VAT per rate (never blended), `amounts.outstanding_cents` what the customer still owes, and `rot_rut` the part claimed from Skatteverket, kept apart from what the customer owes. `is_editable` says whether the invoice may still change. Another organization's invoice id answers 404.","x-scope":"read","x-module":"invoicing","x-permissions":[],"responses":{"200":{"description":"The invoice.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Invoice"}},"additionalProperties":false},"example":{"data":{"id":"01a15b02-7c14-7e2b-8d5a-3f9c1e6b4a27","type":"invoice","invoice_number":"1042","series":"A","reference":"A-1042","status":"sent","status_label":"Skickad","is_editable":false,"handover":null,"customer":{"id":"01a15a21-0c44-7b19-8e2d-6f3a9c1b5d70","name":"Eva Ek","customer_number":"1043","payment_terms_days":20},"contact":{"id":"01a15a21-0c7a-70e4-9b3c-5d8a2f6e1c49","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","account_id":"01a15a21-0c44-7b19-8e2d-6f3a9c1b5d70","email":"eva.ek@example.se","name":"Eva Ek","title":null,"phone":"+46 70 123 45 67","crm_external_id":null,"crm_provider":null,"metadata":null,"created_at":"2026-09-14T10:03:51.000000Z","updated_at":"2026-09-14T10:03:51.000000Z","deleted_at":null},"dates":{"issue_date":"2026-10-18","due_date":"2026-11-07","delivery_date":"2026-10-16","payment_terms_days":20,"days_overdue":0},"references":{"your_reference":"Eva Ek","our_reference":"Anna Lindqvist","ocr_reference":"10421"},"amounts":{"currency":"SEK","net_cents":195000,"vat_cents":48750,"rounding_cents":50,"total_cents":243800,"paid_cents":0,"outstanding_cents":131300,"vat_by_rate":[{"rate":25,"net_cents":195000,"vat_cents":48750}]},"rot_rut":{"type":"rut","work_type":"Stadning","requested_cents":112500,"approved_cents":0,"refused_cents":0,"status":"none","customer_pays_cents":131300,"annual_ceiling_applied":false,"tax_profile_id":null,"buyer":null},"lines":[{"id":"01a15b02-7c2e-7148-a9f3-6d2b8e5c1f40","sort_order":0,"description":"Hemstädning","quantity":4,"unit":"tim","unit_price_cents":45000,"discount_percent":0,"vat_rate":25,"net_cents":180000,"vat_cents":45000,"total_cents":225000,"line_type":"labour","rot_rut_eligible":true,"rot_rut_work_type":"Stadning","is_generated":false,"account_code":"3001","article_id":"01a159f8-2d6a-7b3e-9f1c-8e4a2d7b5c63","work_order_id":null,"time_entry_id":null,"metadata":[]},{"id":"01a15b02-7c41-72d6-b5e8-1a7f3c9d2e85","sort_order":1,"description":"Rengöringsmedel","quantity":1,"unit":"st","unit_price_cents":15000,"discount_percent":0,"vat_rate":25,"net_cents":15000,"vat_cents":3750,"total_cents":18750,"line_type":"goods","rot_rut_eligible":false,"rot_rut_work_type":null,"is_generated":false,"account_code":null,"article_id":null,"work_order_id":null,"time_entry_id":null,"metadata":[]},{"id":"01a15b02-7c53-7a9c-8e1d-4b6f2a8c5d19","sort_order":2,"description":"RUT-avdrag, 50 % av arbetskostnad inkl. moms","quantity":1,"unit":null,"unit_price_cents":-112500,"discount_percent":0,"vat_rate":0,"net_cents":-112500,"vat_cents":0,"total_cents":-112500,"line_type":"rot_rut_deduction","rot_rut_eligible":false,"rot_rut_work_type":null,"is_generated":true,"account_code":null,"article_id":null,"work_order_id":null,"time_entry_id":null,"metadata":{"eligible_gross_cents":225000,"rate_percent":50,"customer_pays_cents":131300,"annual_ceiling_applied":false}}],"delivery_method":"email","delivery":null,"delivery_failure":null,"sent_at":"2026-10-18T09:20:44+00:00","viewed_at":"2026-10-18T17:02:31+00:00","notes":null,"created_at":"2026-10-17T15:02:10+00:00","updated_at":"2026-10-18T09:20:44+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateInvoice","tags":["Invoices"],"summary":"Update an invoice draft","description":"Changes the header of a draft — customer, contact, dates, terms, references, ROT/RUT, delivery method, notes. Only the fields you send change.\n\n- Changing `payment_terms_days` or `issue_date` without sending `due_date` moves the due date with them.\n- Changing `account_id` takes the new customer's payment terms unless you send your own.\n- Changing `rot_rut` adds, recalculates or removes the deduction line at once.\n\nAn issued invoice answers 409: correct it with a credit note.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.invoices.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceFields"},"example":{"payment_terms_days":30,"notes":"Gäller städning vecka 42."}}}},"responses":{"200":{"description":"The draft as it now stands.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Invoice"}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/InvoiceConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteInvoice","tags":["Invoices"],"summary":"Delete an invoice draft","description":"Throws a draft away. A draft never took a number, so nothing is left to explain. An issued invoice cannot be deleted — it has to be kept for seven years — and answers 409; correct it with a credit note instead.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.invoices.manage"],"responses":{"200":{"description":"The draft was deleted.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"boolean","enum":[true]}},"additionalProperties":false},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/InvoiceConflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoices/{invoice}/lines":{"parameters":[{"$ref":"#/components/parameters/InvoiceId"}],"get":{"operationId":"listInvoiceLines","tags":["Invoice lines"],"summary":"List an invoice's lines","description":"Every line of the invoice in printed order, including the generated ROT/RUT line (`is_generated: true`), which always comes last. Not paginated: a page holding part of an invoice is a subtotal nobody can use.","x-scope":"read","x-module":"invoicing","x-permissions":[],"responses":{"200":{"description":"The lines.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceLine"}}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createInvoiceLine","tags":["Invoice lines"],"summary":"Add a line","description":"Adds a line at the end of a draft and recalculates the invoice.\n\n**From an article.** Send `article_id` and the article's name, unit, price, VAT rate, BAS account and ROT/RUT eligibility are copied onto the line. Anything you send beside it wins — an agreed price for one job does not belong in the price list. The line keeps `article_id` for traceability; later changes to the article do not touch it.\n\n**By hand.** `description` is required; `quantity` defaults to 1, `unit_price_cents` (öre, excluding VAT) to 0, `vat_rate` to 25 and `line_type` to `goods`.\n\n**Arithmetic.** net = quantity × unit price × (1 − discount), rounded to the öre; VAT = net × rate. `quantity` may be fractional (2.5 hours, 13.75 m²). `vat_rate` must be 25, 12, 6 or 0 — anything else is a 422 naming the four rates.\n\n**ROT/RUT.** A line counts towards the deduction only if it is `labour` or `service` *and* `rot_rut_eligible` — a material line never does, whatever the flag. The deduction line itself cannot be written (409); set `rot_rut` on the invoice instead.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.invoices.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceLineFields"},"examples":{"article":{"summary":"Four hours of cleaning from the price list","value":{"article_id":"01a159f8-2d6a-7b3e-9f1c-8e4a2d7b5c63","quantity":4}},"manual":{"summary":"A line typed by hand","value":{"description":"Rengöringsmedel","quantity":1,"unit":"st","unit_price_cents":15000,"vat_rate":25,"line_type":"goods"}}}}}},"responses":{"201":{"description":"The line, with its amounts.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/InvoiceLine"}},"additionalProperties":false},"example":{"data":{"id":"01a15b02-7c2e-7148-a9f3-6d2b8e5c1f40","sort_order":0,"description":"Hemstädning","quantity":4,"unit":"tim","unit_price_cents":45000,"discount_percent":0,"vat_rate":25,"net_cents":180000,"vat_cents":45000,"total_cents":225000,"line_type":"labour","rot_rut_eligible":true,"rot_rut_work_type":"Stadning","is_generated":false,"account_code":"3001","article_id":"01a159f8-2d6a-7b3e-9f1c-8e4a2d7b5c63","work_order_id":null,"time_entry_id":null,"metadata":[]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/InvoiceConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoices/{invoice}/lines/reorder":{"parameters":[{"$ref":"#/components/parameters/InvoiceId"}],"post":{"operationId":"reorderInvoiceLines","tags":["Invoice lines"],"summary":"Reorder the lines","description":"Sets the printed order of a draft's lines. `ids` must name every line exactly once — except the generated ROT/RUT line, which always stays last and is not named — or the answer is 409 saying how many were expected. Answers with all lines in their new order.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.invoices.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids"],"properties":{"ids":{"type":"array","minItems":1,"items":{"type":"string","format":"uuid"},"description":"Line ids in the order they should be printed."}},"additionalProperties":false},"example":{"ids":["01a15b02-7c41-72d6-b5e8-1a7f3c9d2e85","01a15b02-7c2e-7148-a9f3-6d2b8e5c1f40"]}}}},"responses":{"200":{"description":"The lines in their new order.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceLine"}}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/InvoiceConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoices/{invoice}/lines/{line}":{"parameters":[{"$ref":"#/components/parameters/InvoiceId"},{"name":"line","in":"path","required":true,"description":"The line's id. It must belong to this invoice, or the answer is 404.","schema":{"type":"string","format":"uuid"}}],"patch":{"operationId":"updateInvoiceLine","tags":["Invoice lines"],"summary":"Update a line","description":"Changes only the fields you send; the line's amounts and the invoice's totals and deduction are recalculated from the result. Sending `article_id` copies that article onto the line again, as when adding one. The generated ROT/RUT line cannot be changed (409), and neither can any line of an issued invoice.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.invoices.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceLineFields"},"example":{"quantity":3.5,"discount_percent":10}}}},"responses":{"200":{"description":"The line as it now stands.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/InvoiceLine"}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/InvoiceConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteInvoiceLine","tags":["Invoice lines"],"summary":"Delete a line","description":"Removes a line from a draft and recalculates the invoice. The generated ROT/RUT line cannot be removed on its own (409) — set `rot_rut: \"none\"` on the invoice to drop it.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.invoices.manage"],"responses":{"200":{"description":"The line was deleted.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"boolean","enum":[true]}},"additionalProperties":false},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/InvoiceConflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoices/{invoice}/send":{"parameters":[{"$ref":"#/components/parameters/InvoiceId"}],"post":{"operationId":"sendInvoice","tags":["Invoices"],"summary":"Send (issue) an invoice","description":"Turns a draft into an invoice and delivers it. This is the irreversible step, and it needs its own permission, `invoicing.invoices.send`.\n\nIn one transaction the invoice:\n1. takes the next number in its series and gets its OCR reference;\n2. is dated today by the Swedish calendar, with the due date `payment_terms_days` later;\n3. has its totals summed from the lines and its ROT/RUT request fixed;\n4. becomes `sent`, and its PDF is rendered and stored — that file is what `GET /invoices/{invoice}/pdf` serves from now on.\n\nThen, for `delivery_method: email`, it is emailed with the PDF attached to `to`, or else to the invoice's contact, or else to the customer's `reminder_email`. `peppol` and `print` are recorded but **not delivered** by Closepact; `delivered` is `null` for them and you deliver the PDF yourself.\n\n**Before anything is spent:** an invoice that is already issued answers 409 (a retried request is therefore safe — it cannot take a second number), an invoice without lines 409, and an email invoice with nowhere to send it 422 on `to`, leaving the draft untouched.\n\nThe response is the issued invoice **as stored** — its database row with `lines`, `account` and `contact` — which is a different shape from `GET /invoices/{invoice}`; fetch that if you want the usual one.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.invoices.send"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":["string","null"],"format":"email","maxLength":320,"description":"Send to this address instead of the contact's."},"delivery_method":{"type":"string","enum":["email","peppol","print"],"description":"Overrides the draft's delivery method, and is saved on it."}},"additionalProperties":false},"example":{"to":"eva.ek@example.se"}}}},"responses":{"200":{"description":"The issued invoice as stored, and where it was emailed.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","delivered"],"properties":{"data":{"$ref":"#/components/schemas/InvoiceRecord"},"delivered":{"type":["string","null"],"description":"The address the invoice was emailed to, or null when nothing was sent (peppol, print)."}},"additionalProperties":false},"example":{"data":{"id":"01a15b02-7c14-7e2b-8d5a-3f9c1e6b4a27","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","sub_entity_id":null,"invoice_number":"1042","series":"A","type":"invoice","credited_invoice_id":null,"account_id":"01a15a21-0c44-7b19-8e2d-6f3a9c1b5d70","contact_id":"01a15a21-0c7a-70e4-9b3c-5d8a2f6e1c49","status":"sent","issue_date":"2026-10-18T00:00:00.000000Z","due_date":"2026-11-07T00:00:00.000000Z","delivery_date":"2026-10-16T00:00:00.000000Z","currency":"SEK","your_reference":"Eva Ek","our_reference":"Anna Lindqvist","payment_terms_days":20,"net_cents":195000,"vat_cents":48750,"rounding_cents":50,"total_cents":243800,"rot_rut":"rut","rot_rut_work_type":"Stadning","rot_rut_requested_cents":112500,"rot_rut_approved_cents":0,"rot_rut_refused_cents":0,"rot_rut_status":"pending","tax_profile_id":null,"paid_cents":0,"ocr_reference":"10421","delivery_method":"email","sent_at":"2026-10-18T09:20:44.000000Z","viewed_at":null,"reminder_count":0,"last_reminder_at":null,"pdf_document_id":"01a15b02-8f61-7c3a-b4d2-9e1f6a3c8b05","sie_exported_at":null,"external_ref":[],"notes":null,"metadata":{"vat_by_rate":[{"rate":25,"net_cents":195000,"vat_cents":48750}],"deduction":{"type":"rut","rate_percent":50,"eligible_gross_cents":225000,"deduction_cents":112500,"customer_pays_cents":131300,"annual_ceiling_applied":false},"delivery":{"method":"email","to":"eva.ek@example.se","at":"2026-10-18T09:20:44+00:00","automatic":false,"status":"queued"}},"created_by":"01a14eb3-c634-7238-bc76-18b09b287f98","created_at":"2026-10-17T15:02:10.000000Z","updated_at":"2026-10-18T09:20:44.000000Z","deleted_at":null,"recurring_invoice_id":null,"occurrence_date":null,"lines":[{"id":"01a15b02-7c2e-7148-a9f3-6d2b8e5c1f40","customer_invoice_id":"01a15b02-7c14-7e2b-8d5a-3f9c1e6b4a27","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","sort_order":0,"article_id":"01a159f8-2d6a-7b3e-9f1c-8e4a2d7b5c63","work_order_id":null,"time_entry_id":null,"description":"Hemstädning","quantity":4,"unit":"tim","unit_price_cents":45000,"discount_percent":0,"vat_rate":25,"net_cents":180000,"vat_cents":45000,"total_cents":225000,"line_type":"labour","rot_rut_eligible":true,"rot_rut_work_type":"Stadning","account_code":"3001","metadata":[],"created_at":"2026-10-17T15:03:40.000000Z","updated_at":"2026-10-17T15:03:40.000000Z"}],"account":{"id":"01a15a21-0c44-7b19-8e2d-6f3a9c1b5d70","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","name":"Eva Ek","domain":null,"industry":null,"size":null,"country":"SE","crm_external_id":null,"crm_provider":null,"metadata":null,"created_at":"2026-09-14T10:03:51.000000Z","updated_at":"2026-09-14T10:03:51.000000Z","deleted_at":null,"sub_entity_id":null,"customer_number":"1043","customer_type":"private","org_number":null,"vat_number":null,"industry_code":null,"payment_terms_days":20,"late_interest_percent":null,"vat_mode":"se","invoice_delivery":"email","peppol_id":null,"einvoice_reference":null,"reminder_email":null,"currency":"SEK","locale":"sv","default_price_list_id":null,"credit_limit_cents":null,"is_blocked":false,"notes":null},"contact":{"id":"01a15a21-0c7a-70e4-9b3c-5d8a2f6e1c49","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","account_id":"01a15a21-0c44-7b19-8e2d-6f3a9c1b5d70","email":"eva.ek@example.se","name":"Eva Ek","title":null,"phone":"+46 70 123 45 67","crm_external_id":null,"crm_provider":null,"metadata":null,"created_at":"2026-09-14T10:03:51.000000Z","updated_at":"2026-09-14T10:03:51.000000Z","deleted_at":null}},"delivered":"eva.ek@example.se"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/InvoiceConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoices/{invoice}/credit":{"parameters":[{"$ref":"#/components/parameters/InvoiceId"}],"post":{"operationId":"creditInvoice","tags":["Invoices"],"summary":"Credit an issued invoice","description":"Corrects an issued invoice by raising a credit note (kreditfaktura) against it. The original is never edited.\n\nThe body chooses how much:\n- **nothing** — the whole invoice, every line mirrored with negative quantities;\n- **`lines`** — some lines, each for its full quantity or for part of it (`quantity`, at most what was invoiced);\n- **`amount_cents`** — an agreed sum (net, in öre, at `vat_rate`, default 25) that corresponds to no line. Not combined with `lines`.\n\nThe credit note is issued immediately: it gets its own number in the credit series (`K` + the invoice's series, so `KA-17`), no OCR reference, today's date and a stored PDF, and it is emailed when the invoice has a recipient — though, unlike sending, a missing address does not stop it. When the invoice has been credited in full it becomes `credited` and is no longer chased.\n\nRefused with 409 for a draft (edit it instead), for a credit note, for an invoice already credited in full, and for more than remains to credit. Whether a ROT/RUT claim already filed with Skatteverket is withdrawn is handled in Closepact's deduction workflow, not by the credit note.\n\nLike sending, the response is the credit note **as stored**, with `credited_invoice` holding the original.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.credit"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":["string","null"],"maxLength":500,"description":"Printed on the credit note."},"lines":{"type":"array","items":{"type":"object","required":["line_id"],"properties":{"line_id":{"type":"string","format":"uuid","description":"A line of this invoice."},"quantity":{"type":"number","exclusiveMinimum":0,"description":"How much of it to credit; the whole line when left out."}},"additionalProperties":false}},"amount_cents":{"type":"integer","minimum":1,"description":"Credit this net amount in öre instead of lines."},"vat_rate":{"type":"number","enum":[25,12,6,0],"default":25,"description":"With `amount_cents`: the VAT rate the amount was charged at."},"description":{"type":["string","null"],"maxLength":500,"description":"With `amount_cents`: the credit line's text. Defaults to *Kreditering av faktura A-1042*."}},"additionalProperties":false},"examples":{"whole":{"summary":"Credit the whole invoice","value":{"reason":"Fakturan skickades till fel kund."}},"partial":{"summary":"One hour less of cleaning","value":{"reason":"Avbokad timme","lines":[{"line_id":"01a15b02-7c2e-7148-a9f3-6d2b8e5c1f40","quantity":1}]}},"amount":{"summary":"An agreed discount","value":{"reason":"Rabatt enligt överenskommelse","amount_cents":20000,"vat_rate":25}}}}}},"responses":{"201":{"description":"The issued credit note as stored, with the invoice it credits.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/InvoiceRecord"}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/InvoiceConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoices/{invoice}/pdf":{"parameters":[{"$ref":"#/components/parameters/InvoiceId"}],"get":{"operationId":"getInvoicePdf","tags":["Invoices"],"summary":"Download the PDF","description":"The invoice document itself, as `application/pdf` bytes in the response body (served `inline`, with a file name in `Content-Disposition`). There is no signed URL to fetch; download it with the API key.\n\n- **Issued invoice or credit note:** exactly the file stored when it was sent — the one the customer received — never re-rendered, so it stays identical for the seven years it has to be kept. If that file has gone missing the answer is 409 rather than a lookalike.\n- **Draft:** a preview rendered on the spot, watermarked, with no number and no OCR reference, named `…-utkast.pdf`. It is not an invoice and must not be handed over as one.\n\nNo extra permission: an organization whose invoicing module has lapsed can still fetch its old invoices for an auditor.","x-scope":"read","x-module":"invoicing","x-permissions":[],"responses":{"200":{"description":"The PDF.","headers":{"Content-Disposition":{"description":"`inline; filename=\"…\"`.","schema":{"type":"string"},"example":"inline; filename=\"Faktura A-1042.pdf\""},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/InvoiceConflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoices/{invoice}/payments":{"parameters":[{"$ref":"#/components/parameters/InvoiceId"}],"get":{"operationId":"listInvoicePayments","tags":["Invoice payments"],"summary":"List an invoice's payments","description":"Every receipt against the invoice, the most recent payment date first, with the invoice's balance in `meta.invoice`. The balance is computed from all payments, not from the page.\n\nBesides receipts from the customer (`from_customer: true`) the list can hold the ROT/RUT settlement from Skatteverket (`method: rot_rut`) and write-offs (`writeoff`), which are recorded in Closepact and not through this API.\n\nPaging is by cursor (`meta.next_cursor`); there are no `links`.","x-scope":"read","x-module":"invoicing","x-permissions":[],"parameters":[{"name":"filter[method]","in":"query","required":false,"description":"One method, or several as `filter[method][]=bank&filter[method][]=card`.","schema":{"oneOf":[{"type":"string","enum":["bank","card","autogiro","cash","rot_rut","writeoff"]},{"type":"array","items":{"type":"string","enum":["bank","card","autogiro","cash","rot_rut","writeoff"]}}]}},{"name":"limit","in":"query","required":false,"description":"Payments per page.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"The payments and the invoice's balance.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/InvoicePayment"}},"meta":{"type":"object","required":["invoice","next_cursor","prev_cursor"],"properties":{"invoice":{"$ref":"#/components/schemas/InvoiceBalance"},"next_cursor":{"type":["string","null"]},"prev_cursor":{"type":["string","null"]}},"additionalProperties":false}},"additionalProperties":false},"example":{"data":[{"id":"01a15c11-9e2f-7d84-b3a6-5c8e1f4d2a97","customer_invoice_id":"01a15b02-7c14-7e2b-8d5a-3f9c1e6b4a27","paid_at":"2026-11-05","amount_cents":131300,"currency":"SEK","method":"bank","from_customer":true,"reference":"10421","bank_statement_line_id":null,"note":null,"created_by":"Anna Lindqvist","created_at":"2026-11-06T07:45:12+00:00"}],"meta":{"invoice":{"id":"01a15b02-7c14-7e2b-8d5a-3f9c1e6b4a27","reference":"A-1042","status":"paid","currency":"SEK","total_cents":243800,"rot_rut_requested_cents":112500,"paid_cents":131300,"outstanding_cents":0,"days_overdue":0},"next_cursor":null,"prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createInvoicePayment","tags":["Invoice payments"],"summary":"Register a payment","description":"Records money received against an issued invoice and updates its status: `paid` once the customer's share is covered, `partially_paid` before that. On a ROT/RUT invoice the customer's share is `total − rot_rut.requested`; Skatteverket's part is settled separately in Closepact.\n\n- `amount_cents` is signed: a refund to the customer is a negative payment. Zero is refused.\n- `paid_at` is the day the money moved (YYYY-MM-DD, default today) — VAT periods and ageing are cut on it, so send the bank's date rather than today's.\n- `currency` must be the invoice's; there is no conversion (409).\n- Put the bank's reference, typically the OCR number, in `reference`.\n\nRefused with 409 for a draft (nothing to pay yet) and for a credited or written-off invoice — money arriving against those belongs on the credit note or a new invoice. The response carries the new balance, so you need not fetch the invoice again.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.payments.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount_cents"],"properties":{"amount_cents":{"type":"integer","not":{"const":0},"description":"Öre. Negative for a refund."},"paid_at":{"type":"string","format":"date","description":"Defaults to today."},"method":{"type":"string","enum":["bank","card","autogiro","cash"],"default":"bank"},"reference":{"type":["string","null"],"maxLength":128},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"Defaults to, and must equal, the invoice's currency."},"note":{"type":["string","null"],"maxLength":1000}},"additionalProperties":false},"example":{"amount_cents":131300,"paid_at":"2026-11-05","method":"bank","reference":"10421"}}}},"responses":{"201":{"description":"The payment, and the invoice's balance after it.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/InvoicePayment"},"meta":{"type":"object","required":["invoice"],"properties":{"invoice":{"$ref":"#/components/schemas/InvoiceBalance"}},"additionalProperties":false}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/InvoiceConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoices/{invoice}/reminders":{"parameters":[{"$ref":"#/components/parameters/InvoiceId"}],"get":{"operationId":"listInvoiceReminders","tags":["Invoices"],"summary":"List an invoice's payment reminders","description":"The payment reminders (påminnelser) sent for the invoice, with the fee and interest each one charged — figures your bookkeeping needs, because the customer was told them — and what the next reminder would be.\n\nThere are three levels and at most one reminder per level. Levels 1 and 2 can go out automatically on the organization's schedule; level 3 announces that the debt goes to collection and is only ever sent by a person in Closepact. `next_level_refusal` says why the next reminder could not be sent now (for example *This invoice is not overdue yet.*), or is null when it could. Sending reminders is not available through the API.","x-scope":"read","x-module":"invoicing","x-permissions":[],"responses":{"200":{"description":"The reminders sent and the next level.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["reminders","next_level","next_level_refusal","automatic_up_to","outstanding_cents","days_overdue"],"properties":{"reminders":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceReminder"}},"next_level":{"type":["integer","null"],"minimum":1,"maximum":3,"description":"Null once all three levels have been sent."},"next_level_refusal":{"type":["string","null"],"description":"Why the next level cannot be sent now; null when it can."},"automatic_up_to":{"type":"integer","description":"The highest level the schedule sends by itself."},"outstanding_cents":{"type":"integer","description":"What the customer still owes, excluding a ROT/RUT share."},"days_overdue":{"type":"integer"}},"additionalProperties":false}},"additionalProperties":false},"example":{"data":{"reminders":[{"id":"01a15d33-4c8e-7b15-a6f9-1d3e7c2b5a84","customer_invoice_id":"01a15b02-7c14-7e2b-8d5a-3f9c1e6b4a27","level":1,"sent_at":"2026-11-15T06:00:12+00:00","fee_cents":6000,"interest_percent":10.5,"interest_cents":302,"channel":"email","created_by":null,"metadata":[],"created_at":"2026-11-15T06:00:12+00:00"}],"next_level":2,"next_level_refusal":null,"automatic_up_to":2,"outstanding_cents":131300,"days_overdue":8}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoice-payments/{payment}":{"parameters":[{"name":"payment","in":"path","required":true,"description":"The payment's id (`id` in the payments list).","schema":{"type":"string","format":"uuid"}}],"delete":{"operationId":"deleteInvoicePayment","tags":["Invoice payments"],"summary":"Remove a payment","description":"Takes back a payment registered in error and recalculates the invoice. A `paid` invoice goes back to `partially_paid` — an issued invoice never returns to `sent` — and if the payment came from a matched bank statement line, that line is released for matching again.\n\nRefused with 409 for a ROT/RUT settlement or a write-off (settled where they were decided), on a credited or written-off invoice, and once the invoice has been exported to the books (SIE) — correct it in the bookkeeping then.","x-scope":"write","x-module":"invoicing","x-permissions":["invoicing.payments.manage"],"responses":{"200":{"description":"The payment was removed; the invoice's balance after it.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["deleted","meta"],"properties":{"deleted":{"type":"boolean","enum":[true]},"meta":{"type":"object","required":["invoice"],"properties":{"invoice":{"$ref":"#/components/schemas/InvoiceBalance"}},"additionalProperties":false}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/InvoiceConflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/products":{"get":{"operationId":"listProducts","tags":["Products"],"summary":"List products","description":"Returns the catalogue as a cursor-paginated list, by name unless you ask for another order, with each product's property values. Page through with `cursor` until `meta.next_cursor` is `null`; `links.next` keeps your search and sort.","x-scope":"read","x-module":"proposals","x-permissions":[],"parameters":[{"name":"q","in":"query","required":false,"description":"Text the name or SKU contains, case-insensitively and with å, ä and ö folded correctly.","schema":{"type":"string"},"example":"städ"},{"name":"active_only","in":"query","required":false,"description":"Only products that are on sale (`is_active`).","schema":{"type":"boolean","default":false}},{"name":"sort","in":"query","required":false,"description":"Sort field. Anything else sorts by name.","schema":{"type":"string","enum":["name","sku","unit_price_cents","created_at","updated_at"],"default":"name"}},{"name":"direction","in":"query","required":false,"description":"Sort direction.","schema":{"type":"string","enum":["asc","desc"],"default":"asc"}},{"name":"limit","in":"query","required":false,"description":"Items per page. Values outside 1–200 are clamped.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of products.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a15b70-6c7d-7e8f-9a0b-1c2d3e4f5a6b","sku":"HEM-VV","name":"Hemstäd varannan vecka","description":"Två timmar per tillfälle.","type":"recurring","unit_price_cents":160000,"vat_rate":25,"currency":"SEK","billing_cycle":"monthly","is_active":true,"service_id":null,"metadata":[],"properties":{"01a15b60-1d2e-7f3a-8b4c-5d6e7f8a9b0c":85,"01a15b61-2e3f-7a4b-9c5d-6e7f8a9b0c1d":true},"created_by_name":"Anna Lindqvist","updated_by_name":"Anna Lindqvist","created_at":"2026-09-01T10:00:00+00:00","updated_at":"2026-10-01T08:15:00+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":null},"meta":{"path":"https://api.closepact.com/v1/api/products","per_page":50,"next_cursor":null,"prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createProduct","tags":["Products"],"summary":"Create a product","description":"Adds a product to the catalogue, on sale unless you say otherwise. The SKU must be unique among the organization's products; reuse of a deleted product's SKU is not supported. Recorded as created by the member the key acts as.\n\nThe SKU makes retries safe: sending the same SKU again is a 422 rather than a second product.","x-scope":"write","x-module":"proposals","x-feature":"proposals.cpq","x-permissions":["cpq.products.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["sku","name","unit_price_cents"],"properties":{"sku":{"type":"string","maxLength":64,"description":"Your article number. Unique within the organization, deleted products included: a deleted product keeps its SKU, and sending a SKU already taken is a 422 on `sku`."},"name":{"type":"string","maxLength":255},"description":{"type":["string","null"],"maxLength":2000},"type":{"oneOf":[{"$ref":"#/components/schemas/ProductType"},{"type":"null"}],"description":"Defaults to `one_time`."},"unit_price_cents":{"type":"integer","minimum":0,"description":"Price per unit in öre, excluding VAT."},"vat_rate":{"type":["number","null"],"enum":[25,12,6,0,null],"default":25,"description":"Moms in percent. Quote lines made from the product inherit it."},"currency":{"type":["string","null"],"minLength":3,"maxLength":3,"default":"SEK","description":"ISO 4217 code, upper case. Stored as sent."},"billing_cycle":{"type":["string","null"],"enum":["monthly","yearly",null],"description":"How often a recurring product is charged."},"is_active":{"type":["boolean","null"],"default":true,"description":"Whether the product is on sale."},"service_id":{"type":["string","null"],"format":"uuid","description":"The service in your scheduling catalogue (`GET /services`) this product is performed as. When an accepted quote becomes a work order, a line made from the product becomes that service on the job. A service of another organization, or a deleted one, is a 422."},"metadata":{"type":["object","null"],"additionalProperties":true,"description":"Free-form values, for example your own system's id."},"properties":{"$ref":"#/components/schemas/ProductPropertyValuesInput"}},"additionalProperties":false},"example":{"sku":"HEM-VV","name":"Hemstäd varannan vecka","description":"Två timmar per tillfälle.","type":"recurring","unit_price_cents":160000,"billing_cycle":"monthly","properties":{"area_sqm":85,"rut":true}}}}},"responses":{"201":{"description":"The product, as created.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductEnvelope"},"example":{"data":{"id":"01a15b70-6c7d-7e8f-9a0b-1c2d3e4f5a6b","sku":"HEM-VV","name":"Hemstäd varannan vecka","description":"Två timmar per tillfälle.","type":"recurring","unit_price_cents":160000,"vat_rate":25,"currency":"SEK","billing_cycle":"monthly","is_active":true,"service_id":null,"metadata":[],"properties":{"01a15b60-1d2e-7f3a-8b4c-5d6e7f8a9b0c":85,"01a15b61-2e3f-7a4b-9c5d-6e7f8a9b0c1d":true},"created_by_name":"Anna Lindqvist","updated_by_name":"Anna Lindqvist","created_at":"2026-09-01T10:00:00+00:00","updated_at":"2026-09-01T10:00:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/products/properties":{"get":{"operationId":"listProductProperties","tags":["Products"],"summary":"List product properties","description":"Returns the custom fields your organization defined for products, in display order. A product's `properties` are keyed by these ids; when writing them you may use the stable `key` instead. Properties are defined in the panel. Not paginated.","x-scope":"read","x-module":"proposals","x-permissions":[],"responses":{"200":{"description":"The product properties.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ProductProperty"}}},"additionalProperties":false},"example":{"data":[{"id":"01a15b60-1d2e-7f3a-8b4c-5d6e7f8a9b0c","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","key":"area_sqm","label":"Yta (m²)","type":"number","options":[],"is_required":false,"position":1,"created_at":"2026-08-20T12:00:00.000000Z","updated_at":"2026-08-20T12:00:00.000000Z","deleted_at":null},{"id":"01a15b62-3f4a-7b5c-8d6e-7f8a9b0c1d2e","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","key":"frequency","label":"Intervall","type":"select","options":["Varje vecka","Varannan vecka","Var fjärde vecka"],"is_required":true,"position":2,"created_at":"2026-08-20T12:01:00.000000Z","updated_at":"2026-08-20T12:01:00.000000Z","deleted_at":null}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/products/{product}":{"parameters":[{"$ref":"#/components/parameters/ProductId"}],"get":{"operationId":"getProduct","tags":["Products"],"summary":"Retrieve a product","description":"Returns one product with its property values.","x-scope":"read","x-module":"proposals","x-permissions":[],"responses":{"200":{"description":"The product.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductEnvelope"},"example":{"data":{"id":"01a15b71-7d8e-7f9a-ab1c-2d3e4f5a6b7c","sku":"FL-1","name":"Flyttstäd","description":null,"type":"one_time","unit_price_cents":540000,"vat_rate":25,"currency":"SEK","billing_cycle":null,"is_active":true,"service_id":null,"metadata":{"fortnox_article":"1004"},"properties":[],"created_by_name":"Anna Lindqvist","updated_by_name":null,"created_at":"2026-09-01T10:05:00+00:00","updated_at":"2026-09-01T10:05:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateProduct","tags":["Products"],"summary":"Update a product","description":"Changes the fields you send and leaves the rest alone, recording the member the key acts as as the last to update it. `metadata` is replaced as a whole; `properties` changes only the properties you list (send `null` to clear one).\n\nQuote lines already made from the product keep the name and price they were made with. Set `is_active` to false to take a product off sale without deleting it.","x-scope":"write","x-module":"proposals","x-feature":"proposals.cpq","x-permissions":["cpq.products.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sku":{"type":"string","maxLength":64,"description":"Unique within the organization, deleted products included; a SKU another product holds is a 422."},"name":{"type":"string","maxLength":255},"description":{"type":["string","null"],"maxLength":2000},"type":{"$ref":"#/components/schemas/ProductType"},"unit_price_cents":{"type":"integer","minimum":0},"vat_rate":{"type":"number","enum":[25,12,6,0],"description":"Applies to lines added from now on; lines already on quotes keep the rate they were made with."},"currency":{"type":"string","minLength":3,"maxLength":3},"billing_cycle":{"type":["string","null"],"enum":["monthly","yearly",null]},"is_active":{"type":"boolean"},"service_id":{"type":["string","null"],"format":"uuid","description":"The scheduling service the product is performed as; null unlinks it. Read when a quote is accepted, so quotes already accepted are not affected."},"metadata":{"type":"object","additionalProperties":true},"properties":{"$ref":"#/components/schemas/ProductPropertyValuesInput"}},"additionalProperties":false},"example":{"unit_price_cents":170000,"properties":{"area_sqm":null}}}}},"responses":{"200":{"description":"The product, as it is now.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductEnvelope"},"example":{"data":{"id":"01a15b70-6c7d-7e8f-9a0b-1c2d3e4f5a6b","sku":"HEM-VV","name":"Hemstäd varannan vecka","description":"Två timmar per tillfälle.","type":"recurring","unit_price_cents":170000,"vat_rate":25,"currency":"SEK","billing_cycle":"monthly","is_active":true,"service_id":null,"metadata":[],"properties":{"01a15b61-2e3f-7a4b-9c5d-6e7f8a9b0c1d":true},"created_by_name":"Anna Lindqvist","updated_by_name":"Erik Sjöberg","created_at":"2026-09-01T10:00:00+00:00","updated_at":"2026-10-18T07:55:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteProduct","tags":["Products"],"summary":"Delete a product","description":"Removes the product from the catalogue: it leaves lists and lookups at once. Quote lines made from it keep their name, price and `product_id`. To stop selling something you may want to sell again, set `is_active` to false instead.","x-scope":"write","x-module":"proposals","x-feature":"proposals.cpq","x-permissions":["cpq.products.manage"],"responses":{"204":{"description":"The product was deleted. No body."},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/proposals":{"get":{"operationId":"listProposals","tags":["Proposals"],"summary":"List proposals","description":"Returns the organization's proposals, most recently changed first, with their full content, as a cursor-paginated list. Page through with `cursor` until `meta.next_cursor` is `null`; `links.next` keeps your filters.","x-scope":"read","x-module":"proposals","x-permissions":[],"parameters":[{"name":"deal_id","in":"query","required":false,"description":"Only proposals for this deal.","schema":{"type":"string","format":"uuid"}},{"name":"deal_room_id","in":"query","required":false,"description":"Only proposals for this room.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/SalesLimit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of proposals.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Proposal"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a15c60-4e5f-7a6b-8c7d-9e0f1a2b3c4d","title":"Förslag hemstädning","status":"published","current_version":3,"deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","blocks":[{"type":"heading","attrs":{"level":1},"content":[{"type":"text","text":"Hemstäd för familjen Ny"}]},{"type":"paragraph","content":[{"type":"text","text":"Varannan vecka, två timmar per tillfälle."}]}],"settings":[],"published_at":"2026-10-14T07:30:00+00:00","created_at":"2026-10-12T09:00:00+00:00","updated_at":"2026-10-14T07:30:00+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":null},"meta":{"path":"https://api.closepact.com/v1/api/proposals","per_page":25,"next_cursor":null,"prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createProposal","tags":["Proposals"],"summary":"Create a proposal","description":"Creates a draft proposal, optionally with content, and saves it as version 1 (\"Initial draft\") under the member the key acts as.\n\nBlocks are checked before anything is saved: an unknown block `type` anywhere in the tree is a 422. Keys a block does not support are dropped, and `text` longer than 10 000 characters is cut. Not idempotent: each call creates another proposal.","x-scope":"write","x-module":"proposals","x-permissions":["proposals.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title"],"properties":{"title":{"type":"string","maxLength":255},"deal_id":{"type":["string","null"],"format":"uuid","description":"A deal in this organization."},"deal_room_id":{"type":["string","null"],"format":"uuid","description":"A room in this organization."},"blocks":{"type":["array","null"],"items":{"$ref":"#/components/schemas/ProposalBlock"},"description":"The content. Leave out for an empty proposal."},"settings":{"type":["object","null"],"additionalProperties":true,"description":"Display settings used by the editor."}},"additionalProperties":false},"example":{"title":"Förslag hemstädning","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","blocks":[{"type":"heading","attrs":{"level":1},"content":[{"type":"text","text":"Hemstäd för familjen Ny"}]},{"type":"paragraph","content":[{"type":"text","text":"Varannan vecka, två timmar per tillfälle."}]}]}}}},"responses":{"201":{"description":"The proposal, as created.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposalEnvelope"},"example":{"data":{"id":"01a15c60-4e5f-7a6b-8c7d-9e0f1a2b3c4d","title":"Förslag hemstädning","status":"draft","current_version":1,"deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":null,"blocks":[{"type":"heading","attrs":{"level":1},"content":[{"type":"text","text":"Hemstäd för familjen Ny"}]},{"type":"paragraph","content":[{"type":"text","text":"Varannan vecka, två timmar per tillfälle."}]}],"settings":[],"published_at":null,"created_at":"2026-10-12T09:00:00+00:00","updated_at":"2026-10-12T09:00:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/proposals/{proposal}":{"parameters":[{"$ref":"#/components/parameters/ProposalId"}],"get":{"operationId":"getProposal","tags":["Proposals"],"summary":"Retrieve a proposal","description":"Returns one proposal with its current content.","x-scope":"read","x-module":"proposals","x-permissions":[],"responses":{"200":{"description":"The proposal.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposalEnvelope"},"example":{"data":{"id":"01a15c60-4e5f-7a6b-8c7d-9e0f1a2b3c4d","title":"Förslag hemstädning","status":"draft","current_version":2,"deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":null,"blocks":[{"type":"heading","attrs":{"level":1},"content":[{"type":"text","text":"Hemstäd för familjen Ny"}]},{"type":"bulletList","content":[{"type":"listItem","content":[{"type":"paragraph","content":[{"type":"text","text":"Dammsugning och våttorkning av golv"}]}]},{"type":"listItem","content":[{"type":"paragraph","content":[{"type":"text","text":"Rengöring av kök och badrum"}]}]}]}],"settings":[],"published_at":null,"created_at":"2026-10-12T09:00:00+00:00","updated_at":"2026-10-12T10:15:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateProposal","tags":["Proposals"],"summary":"Replace a proposal's content","description":"Replaces the whole block tree — send the complete content, not a change — and saves it as a new version: `current_version` goes up by one and a snapshot is kept with your `notes`, under the member the key acts as. The title is not changed here; see [Rename a proposal](/developers/reference/proposals#renameProposal).\n\nThe same block checks as on create apply; nothing is saved when one fails. A published proposal can be edited too, and stays published.","x-scope":"write","x-module":"proposals","x-permissions":["proposals.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["blocks"],"properties":{"blocks":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/ProposalBlock"}},"notes":{"type":["string","null"],"maxLength":255,"description":"What changed, kept with the version."}},"additionalProperties":false},"example":{"blocks":[{"type":"heading","attrs":{"level":1},"content":[{"type":"text","text":"Hemstäd för familjen Ny"}]},{"type":"paragraph","content":[{"type":"text","text":"Nu med fönsterputs två gånger per år."}]}],"notes":"La till fönsterputs"}}}},"responses":{"200":{"description":"The proposal at its new version.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposalEnvelope"},"example":{"data":{"id":"01a15c60-4e5f-7a6b-8c7d-9e0f1a2b3c4d","title":"Förslag hemstädning","status":"draft","current_version":2,"deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":null,"blocks":[{"type":"heading","attrs":{"level":1},"content":[{"type":"text","text":"Hemstäd för familjen Ny"}]},{"type":"paragraph","content":[{"type":"text","text":"Nu med fönsterputs två gånger per år."}]}],"settings":[],"published_at":null,"created_at":"2026-10-12T09:00:00+00:00","updated_at":"2026-10-12T10:15:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteProposal","tags":["Proposals"],"summary":"Delete a proposal","description":"Deletes the proposal: it leaves lists and lookups at once and a later request for it is a 404. No webhook fires.","x-scope":"write","x-module":"proposals","x-permissions":["proposals.delete"],"responses":{"204":{"description":"The proposal was deleted. No body."},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/proposals/{proposal}/rename":{"parameters":[{"$ref":"#/components/parameters/ProposalId"}],"patch":{"operationId":"renameProposal","tags":["Proposals"],"summary":"Rename a proposal","description":"Changes the title. The content and version are not touched, and no new version is saved.","x-scope":"write","x-module":"proposals","x-permissions":["proposals.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title"],"properties":{"title":{"type":"string","minLength":1,"maxLength":255}},"additionalProperties":false},"example":{"title":"Förslag hemstäd 2027"}}}},"responses":{"200":{"description":"The renamed proposal.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposalEnvelope"},"example":{"data":{"id":"01a15c60-4e5f-7a6b-8c7d-9e0f1a2b3c4d","title":"Förslag hemstäd 2027","status":"draft","current_version":2,"deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":null,"blocks":[{"type":"paragraph","content":[{"type":"text","text":"Nu med fönsterputs två gånger per år."}]}],"settings":[],"published_at":null,"created_at":"2026-10-12T09:00:00+00:00","updated_at":"2026-10-12T10:20:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/proposals/{proposal}/publish":{"parameters":[{"$ref":"#/components/parameters/ProposalId"}],"post":{"operationId":"publishProposal","tags":["Proposals"],"summary":"Publish a proposal","description":"Marks the proposal `published` and sets `published_at` to now; calling it again moves `published_at` to the new now. Nobody is e-mailed and no webhook fires — to put a proposal in front of a buyer, add it to a room as a `proposal` content block. No request body.","x-scope":"write","x-module":"proposals","x-permissions":["proposals.send"],"responses":{"200":{"description":"The published proposal.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposalEnvelope"},"example":{"data":{"id":"01a15c60-4e5f-7a6b-8c7d-9e0f1a2b3c4d","title":"Förslag hemstäd 2027","status":"published","current_version":2,"deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":null,"blocks":[{"type":"paragraph","content":[{"type":"text","text":"Nu med fönsterputs två gånger per år."}]}],"settings":[],"published_at":"2026-10-14T07:30:00+00:00","created_at":"2026-10-12T09:00:00+00:00","updated_at":"2026-10-14T07:30:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/quotes":{"get":{"operationId":"listQuotes","tags":["Quotes"],"summary":"List quotes","description":"Returns the organization's quotes, newest first, each with its lines, as a cursor-paginated list. Page through with `cursor` until `meta.next_cursor` is `null`; `links.next` keeps your filters.","x-scope":"read","x-module":"proposals","x-permissions":[],"parameters":[{"name":"deal_id","in":"query","required":false,"description":"Only quotes for this deal.","schema":{"type":"string","format":"uuid"}},{"name":"deal_room_id","in":"query","required":false,"description":"Only quotes placed in this room.","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"description":"Only quotes with this status.","schema":{"$ref":"#/components/schemas/QuoteStatus"}},{"$ref":"#/components/parameters/SalesLimit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of quotes.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Quote"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a15c50-8d9e-7f0a-b1c2-d3e4f5a6b7c8","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","quote_number":"Q-00042","title":"Offert hemstädning – familjen Ny","status":"sent","subtotal_cents":320000,"discount_cents":0,"discount_percent":0,"tax_cents":80000,"tax_percent":25,"total_cents":400000,"vat_breakdown":[{"rate":25,"base_cents":320000,"vat_cents":80000}],"rot_rut":{"type":"rut","rate_percent":50,"labour_cents":400000,"deduction_cents":200000,"customer_pays_cents":200000,"preliminary":true,"annual_ceiling_applied":false},"currency":"SEK","expires_at":"2026-11-17","accepted_at":null,"accepted_by_stakeholder_id":null,"acceptance":null,"rejection_reason":null,"share_url":"https://app.closepact.com/q/k3Jq8vR2mXw9TzL5pN7cH4bY6sD1fG0aE8uQ3iO5rV2tW9xZ","handover":{"available":true,"target":"work_order","mode":"automatic","work_order":null,"invoice":null,"blocked_reason":null},"lines":[{"id":"01a15c52-1a2b-7c3d-8e4f-5a6b7c8d9e0f","product_id":"01a15b70-6c7d-7e8f-9a0b-1c2d3e4f5a6b","name":"Hemstäd varannan vecka","description":"Två timmar per tillfälle, RUT-avdrag tillämpas på fakturan.","quantity":2,"unit_price_cents":160000,"discount_percent":0,"vat_rate":25,"line_total_cents":320000,"net_cents":320000,"vat_cents":80000,"line_type":"labour","is_optional":false,"is_selected":true,"billing_cycle":"monthly","position":0}],"created_at":"2026-10-13T08:30:00+00:00","updated_at":"2026-10-13T09:05:12+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":null},"meta":{"path":"https://api.closepact.com/v1/api/quotes","per_page":25,"next_cursor":null,"prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createQuote","tags":["Quotes"],"summary":"Create a quote","description":"Creates an empty draft quote with the next number in the organization's series (`Q-00001`, `Q-00002`, …), then add lines with [Add a line](/developers/reference/quotes#addQuoteLine). Place it in a room with `deal_room_id` so the room's stakeholders can accept it once it is sent.\n\nNot idempotent: each call creates another quote and uses up a number.","x-scope":"write","x-module":"proposals","x-feature":"proposals.cpq","x-permissions":["cpq.quotes.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title"],"properties":{"title":{"type":"string","maxLength":255},"deal_id":{"type":["string","null"],"format":"uuid","description":"A deal in this organization."},"deal_room_id":{"type":["string","null"],"format":"uuid","description":"A room in this organization, whose stakeholders will see the quote once it is sent."},"currency":{"type":["string","null"],"minLength":3,"maxLength":3,"default":"SEK","description":"ISO 4217 code, upper case. Stored as sent."},"discount_percent":{"type":["integer","null"],"minimum":0,"maximum":100,"default":0,"description":"Discount on the whole quote, in whole percent. Applied to each line before its VAT."},"rot_rut":{"type":["string","null"],"enum":["none","rot","rut",null],"default":"none","description":"The tax deduction to estimate on the quote's `labour` lines: `rot` (30 %), `rut` (50 %) or `none`."},"tax_percent":{"type":["integer","null"],"minimum":0,"maximum":100,"deprecated":true,"description":"Deprecated: VAT is set per line (`vat_rate` on each line). Still accepted so older integrations keep working, but it changes no amount."},"expires_at":{"type":["string","null"],"format":"date","description":"Last day the quote can be accepted. Must be after today."}},"additionalProperties":false},"example":{"title":"Offert hemstädning – familjen Ny","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","rot_rut":"rut","expires_at":"2026-11-17"}}}},"responses":{"201":{"description":"The quote, as created.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteEnvelope"},"example":{"data":{"id":"01a15c50-8d9e-7f0a-b1c2-d3e4f5a6b7c8","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","quote_number":"Q-00042","title":"Offert hemstädning – familjen Ny","status":"draft","subtotal_cents":0,"discount_cents":0,"discount_percent":0,"tax_cents":0,"tax_percent":0,"total_cents":0,"vat_breakdown":[],"rot_rut":{"type":"rut","rate_percent":50,"labour_cents":0,"deduction_cents":0,"customer_pays_cents":0,"preliminary":true,"annual_ceiling_applied":false},"currency":"SEK","expires_at":"2026-11-17","accepted_at":null,"accepted_by_stakeholder_id":null,"acceptance":null,"rejection_reason":null,"share_url":null,"handover":{"available":true,"target":"work_order","mode":"automatic","work_order":null,"invoice":null,"blocked_reason":null},"lines":[],"created_at":"2026-10-13T08:30:00+00:00","updated_at":"2026-10-13T08:30:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/quotes/{quote}":{"parameters":[{"$ref":"#/components/parameters/QuoteId"}],"get":{"operationId":"getQuote","tags":["Quotes"],"summary":"Retrieve a quote","description":"Returns one quote with its lines in order. Poll this (or listen for the `quote.accepted` webhook) to learn whether the buyer accepted or rejected it: `status`, `accepted_at`, `acceptance` and `rejection_reason` say so. Once accepted, `acceptance` records who accepted, through which door, the optional lines they ticked and the totals at that moment; the lines' `is_selected` and the quote's totals reflect the same choice.","x-scope":"read","x-module":"proposals","x-permissions":[],"responses":{"200":{"description":"The quote.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteEnvelope"},"example":{"data":{"id":"01a15c50-8d9e-7f0a-b1c2-d3e4f5a6b7c8","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","quote_number":"Q-00042","title":"Offert hemstädning – familjen Ny","status":"accepted","subtotal_cents":387500,"discount_cents":0,"discount_percent":0,"tax_cents":96875,"tax_percent":25,"total_cents":484375,"vat_breakdown":[{"rate":25,"base_cents":387500,"vat_cents":96875}],"rot_rut":{"type":"rut","rate_percent":50,"labour_cents":484375,"deduction_cents":242188,"customer_pays_cents":242187,"preliminary":true,"annual_ceiling_applied":false},"currency":"SEK","expires_at":"2026-11-17","accepted_at":"2026-10-15T19:44:02+00:00","accepted_by_stakeholder_id":"01a15c47-2b3c-7d4e-8f5a-6b7c8d9e0f1a","acceptance":{"via":"room","name":"Anna Ny","email":"anna.ny@example.se","selected_optional_line_ids":["01a15c53-2b3c-7d4e-8f5a-6b7c8d9e0f1b"],"total_cents":484375,"tax_cents":96875,"customer_pays_cents":242187},"rejection_reason":null,"share_url":"https://app.closepact.com/q/k3Jq8vR2mXw9TzL5pN7cH4bY6sD1fG0aE8uQ3iO5rV2tW9xZ","handover":{"available":true,"target":"work_order","mode":"automatic","work_order":{"id":"01a1c0b2-7d4e-7a19-8c35-2f9e6b1d4a07","order_number":"AO-10483","status":"booked"},"invoice":null,"blocked_reason":null},"lines":[{"id":"01a15c52-1a2b-7c3d-8e4f-5a6b7c8d9e0f","product_id":"01a15b70-6c7d-7e8f-9a0b-1c2d3e4f5a6b","name":"Hemstäd varannan vecka","description":"Två timmar per tillfälle, RUT-avdrag tillämpas på fakturan.","quantity":2,"unit_price_cents":160000,"discount_percent":0,"vat_rate":25,"line_total_cents":320000,"net_cents":320000,"vat_cents":80000,"line_type":"labour","is_optional":false,"is_selected":true,"billing_cycle":"monthly","position":0},{"id":"01a15c53-2b3c-7d4e-8f5a-6b7c8d9e0f1b","product_id":null,"name":"Fönsterputs","description":"Tillval: alla fönster, in- och utsida.","quantity":1.5,"unit_price_cents":45000,"discount_percent":0,"vat_rate":25,"line_total_cents":67500,"net_cents":67500,"vat_cents":16875,"line_type":"labour","is_optional":true,"is_selected":true,"billing_cycle":null,"position":1}],"created_at":"2026-10-13T08:30:00+00:00","updated_at":"2026-10-15T19:44:02+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateQuote","tags":["Quotes"],"summary":"Update a quote","description":"Changes the title, quote-level discount, ROT/RUT estimate or expiry, and recomputes the totals. Fields you leave out are kept. VAT is set on each line, not here.\n\nOnly a draft can change. On a `sent` or `expired` quote only `expires_at` may be sent — extending an offer does not change what the buyer would accept — and anything else answers 409 `quote_not_draft`: [revert it to draft](/developers/reference/quotes#revertQuoteToDraft) first, which withdraws the offer until you send it again. An `accepted` or `rejected` quote cannot change at all (409 `quote_locked`); create a new one instead.","x-scope":"write","x-module":"proposals","x-feature":"proposals.cpq","x-permissions":["cpq.quotes.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","maxLength":255},"discount_percent":{"type":"integer","minimum":0,"maximum":100},"rot_rut":{"type":"string","enum":["none","rot","rut"],"description":"The deduction to estimate: `rot` (30 %), `rut` (50 %) or `none`."},"tax_percent":{"type":"integer","minimum":0,"maximum":100,"deprecated":true,"description":"Deprecated: VAT is set per line. Accepted for older integrations, but it changes no amount."},"expires_at":{"type":["string","null"],"format":"date","description":"Last day the quote can be accepted, or null for no limit. Moving it does not change the status of a quote that has already expired."}},"additionalProperties":false},"example":{"discount_percent":10}}}},"responses":{"200":{"description":"The quote, with recomputed totals.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteEnvelope"},"example":{"data":{"id":"01a15c50-8d9e-7f0a-b1c2-d3e4f5a6b7c8","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","quote_number":"Q-00042","title":"Offert hemstädning – familjen Ny","status":"draft","subtotal_cents":320000,"discount_cents":32000,"discount_percent":10,"tax_cents":72000,"tax_percent":25,"total_cents":360000,"vat_breakdown":[{"rate":25,"base_cents":288000,"vat_cents":72000}],"rot_rut":{"type":"rut","rate_percent":50,"labour_cents":360000,"deduction_cents":180000,"customer_pays_cents":180000,"preliminary":true,"annual_ceiling_applied":false},"currency":"SEK","expires_at":"2026-11-17","accepted_at":null,"accepted_by_stakeholder_id":null,"acceptance":null,"rejection_reason":null,"share_url":null,"handover":{"available":true,"target":"work_order","mode":"automatic","work_order":null,"invoice":null,"blocked_reason":null},"lines":[{"id":"01a15c52-1a2b-7c3d-8e4f-5a6b7c8d9e0f","product_id":"01a15b70-6c7d-7e8f-9a0b-1c2d3e4f5a6b","name":"Hemstäd varannan vecka","description":null,"quantity":2,"unit_price_cents":160000,"discount_percent":0,"vat_rate":25,"line_total_cents":320000,"net_cents":288000,"vat_cents":72000,"line_type":"labour","is_optional":false,"is_selected":true,"billing_cycle":"monthly","position":0}],"created_at":"2026-10-13T08:30:00+00:00","updated_at":"2026-10-13T08:52:19+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/QuoteConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteQuote","tags":["Quotes"],"summary":"Delete a quote","description":"Deletes the quote: it leaves lists and lookups at once and a later request for it is a 404. Its number is not reused. No webhook fires.\n\nAn `accepted` quote cannot be deleted (409 `quote_locked`): it is the record of what the buyer agreed to.","x-scope":"write","x-module":"proposals","x-feature":"proposals.cpq","x-permissions":["cpq.quotes.create"],"responses":{"204":{"description":"The quote was deleted. No body."},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/QuoteConflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/quotes/{quote}/send":{"parameters":[{"$ref":"#/components/parameters/QuoteId"}],"post":{"operationId":"sendQuote","tags":["Quotes"],"summary":"Send a quote","description":"Marks a draft quote `sent` and gives it its own link, `share_url`: the buyer opens the quote there, ticks the options they want and accepts or rejects it — no deal room needed. A sent quote in a room also appears on the room's buyer page. Only a sent quote can be accepted or rejected, and only until the end of its `expires_at` day; acceptance fires the `quote.accepted` webhook.\n\nWith `email`, the link is e-mailed to that address (with `name` and `message` in the letter). Without it nothing is mailed — copy `share_url` into your own message, or rely on the room. Calling this again on a sent quote keeps its link and can mail it to someone else.\n\nA draft whose `expires_at` has already passed answers 409 `quote_expired`: move the date first. Asking to e-mail a quote that can no longer be answered (accepted, rejected or past its expiry) answers 409 too. Otherwise idempotent: a quote that is not a draft is returned unchanged. The body is optional.","x-scope":"write","x-module":"proposals","x-feature":"proposals.cpq","x-permissions":["cpq.quotes.create"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":["string","null"],"format":"email","maxLength":255,"description":"Mail the quote's link to this address."},"name":{"type":["string","null"],"maxLength":120,"description":"The recipient's name, for the greeting."},"message":{"type":["string","null"],"maxLength":2000,"description":"A note quoted in the letter."}},"additionalProperties":false},"example":{"email":"anna.ny@example.se","name":"Anna Ny","message":"Hej Anna! Här är offerten vi pratade om."}}}},"responses":{"200":{"description":"The quote.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteEnvelope"},"example":{"data":{"id":"01a15c50-8d9e-7f0a-b1c2-d3e4f5a6b7c8","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","quote_number":"Q-00042","title":"Offert hemstädning – familjen Ny","status":"sent","subtotal_cents":320000,"discount_cents":0,"discount_percent":0,"tax_cents":80000,"tax_percent":25,"total_cents":400000,"vat_breakdown":[{"rate":25,"base_cents":320000,"vat_cents":80000}],"rot_rut":{"type":"rut","rate_percent":50,"labour_cents":400000,"deduction_cents":200000,"customer_pays_cents":200000,"preliminary":true,"annual_ceiling_applied":false},"currency":"SEK","expires_at":"2026-11-17","accepted_at":null,"accepted_by_stakeholder_id":null,"acceptance":null,"rejection_reason":null,"share_url":"https://app.closepact.com/q/k3Jq8vR2mXw9TzL5pN7cH4bY6sD1fG0aE8uQ3iO5rV2tW9xZ","handover":{"available":true,"target":"work_order","mode":"automatic","work_order":null,"invoice":null,"blocked_reason":null},"lines":[{"id":"01a15c52-1a2b-7c3d-8e4f-5a6b7c8d9e0f","product_id":"01a15b70-6c7d-7e8f-9a0b-1c2d3e4f5a6b","name":"Hemstäd varannan vecka","description":null,"quantity":2,"unit_price_cents":160000,"discount_percent":0,"vat_rate":25,"line_total_cents":320000,"net_cents":320000,"vat_cents":80000,"line_type":"labour","is_optional":false,"is_selected":true,"billing_cycle":"monthly","position":0}],"created_at":"2026-10-13T08:30:00+00:00","updated_at":"2026-10-13T09:05:12+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/QuoteConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/quotes/{quote}/revert-to-draft":{"parameters":[{"$ref":"#/components/parameters/QuoteId"}],"post":{"operationId":"revertQuoteToDraft","tags":["Quotes"],"summary":"Revert a quote to draft","description":"Takes a `sent` or `expired` quote back to `draft` so its lines and terms can be changed. This withdraws the offer: a draft cannot be accepted, so it leaves the room's buyer page and its `share_url` stops working, until you [send](/developers/reference/quotes#sendQuote) the revised quote again — with a new link, so nobody can accept the revision from a link to the old offer.\n\nAn `accepted` or `rejected` quote cannot be reverted (409 `quote_locked`). Idempotent on a draft. No request body.","x-scope":"write","x-module":"proposals","x-feature":"proposals.cpq","x-permissions":["cpq.quotes.create"],"responses":{"200":{"description":"The quote, now a draft.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteEnvelope"},"example":{"data":{"id":"01a15c50-8d9e-7f0a-b1c2-d3e4f5a6b7c8","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","quote_number":"Q-00042","title":"Offert hemstädning – familjen Ny","status":"draft","subtotal_cents":320000,"discount_cents":0,"discount_percent":0,"tax_cents":80000,"tax_percent":25,"total_cents":400000,"vat_breakdown":[{"rate":25,"base_cents":320000,"vat_cents":80000}],"rot_rut":{"type":"rut","rate_percent":50,"labour_cents":400000,"deduction_cents":200000,"customer_pays_cents":200000,"preliminary":true,"annual_ceiling_applied":false},"currency":"SEK","expires_at":"2026-11-17","accepted_at":null,"accepted_by_stakeholder_id":null,"acceptance":null,"rejection_reason":null,"share_url":null,"handover":{"available":true,"target":"work_order","mode":"automatic","work_order":null,"invoice":null,"blocked_reason":null},"lines":[{"id":"01a15c52-1a2b-7c3d-8e4f-5a6b7c8d9e0f","product_id":"01a15b70-6c7d-7e8f-9a0b-1c2d3e4f5a6b","name":"Hemstäd varannan vecka","description":null,"quantity":2,"unit_price_cents":160000,"discount_percent":0,"vat_rate":25,"line_total_cents":320000,"net_cents":320000,"vat_cents":80000,"line_type":"labour","is_optional":false,"is_selected":true,"billing_cycle":"monthly","position":0}],"created_at":"2026-10-13T08:30:00+00:00","updated_at":"2026-10-14T10:12:40+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/QuoteConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/quotes/{quote}/convert":{"parameters":[{"$ref":"#/components/parameters/QuoteId"}],"post":{"operationId":"convertQuote","tags":["Quotes"],"summary":"Hand over an accepted quote","description":"Turns an `accepted` quote into what your organization does with one: a **work order** waiting to be planned when you have the scheduling module, otherwise an **invoice draft** when you have the invoicing module — the panel's *Create job* or *Create invoice draft* button. Usually it happens by itself the moment the buyer accepts; call this when your organization hands quotes over manually (`handover.mode` is `manual`), or when acceptance could not do it (`handover.blocked_reason`).\n\nWhat the buyer agreed to is carried over as agreed: the customer, the contact who accepted, the title, the lines they accepted (optional lines only if they ticked them) at the quoted quantities, prices, discounts and VAT rates, and the ROT/RUT choice. A line made from a product linked to a service (`service_id` on the product) becomes that service on the work order; other work becomes a service line of free text and materials become material lines. The work order is `booked` with no time on it, at the customer's default service address. An invoice draft has no number and is never sent by this call. What could not be filled in is listed in the work order's or draft's `handover.warnings`.\n\nA quote is handed over once. If it already was — by acceptance, or an earlier call — the answer is 200 with the same quote and nothing new is made; a work order deleted afterwards can be made again with this call. `handover` in the response says what the quote became.\n\nNeeds the quote-to-job capability (`proposals.convert`, proposals Väx and Pro), and the member the key acts as must be allowed to create work orders (`booking.work_orders.manage`) or, where quotes become invoices, invoice drafts (`invoicing.invoices.manage`). Refused with 409 `quote_not_accepted` for a quote that is not accepted and `handover_unavailable` without either module; with 422 on `account_id` when the quote is tied to no customer and none was given.","x-scope":"write","x-module":"proposals","x-feature":"proposals.convert","x-permissions":["booking.work_orders.manage","invoicing.invoices.manage"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"account_id":{"type":["string","null"],"format":"uuid","description":"The customer the work order or draft is for. Defaults to the quote's: the customer of its deal (or its room's deal), else the customer of the contact who accepted. Required when the quote has none."},"service_address_id":{"type":["string","null"],"format":"uuid","description":"One of the customer's addresses, where the work happens. Defaults to the customer's default service address, or its only one."}},"additionalProperties":false},"example":{"account_id":"01a15a77-0d3b-7e21-a6f9-3c8e2b1d4a05"}}}},"responses":{"201":{"description":"The work order or invoice draft was made now; the quote's `handover` points at it.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteEnvelope"},"example":{"data":{"id":"01a1bf90-2c6a-7e45-9d18-4b7f3a0e6c21","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":null,"quote_number":"Q-2026-0142","title":"Fönsterbyte Solna – offert","status":"accepted","subtotal_cents":1200000,"discount_cents":0,"discount_percent":0,"tax_cents":300000,"tax_percent":25,"total_cents":1500000,"vat_breakdown":[{"rate":25,"base_cents":1200000,"vat_cents":300000}],"rot_rut":{"type":"rot","rate_percent":30,"labour_cents":500000,"deduction_cents":150000,"customer_pays_cents":1350000,"preliminary":true,"annual_ceiling_applied":false},"currency":"SEK","expires_at":"2026-11-15","accepted_at":"2026-10-18T08:41:12+00:00","accepted_by_stakeholder_id":null,"acceptance":{"via":"link","name":"Eva Ek","email":"eva.ek@example.se","selected_optional_line_ids":[],"total_cents":1500000,"tax_cents":300000,"customer_pays_cents":1350000},"rejection_reason":null,"share_url":"https://app.closepact.com/q/k3Jq8vR2mXw9TzL5pN7cH4bY6sD1fG0aE8uQ3iO5rV2tW9xZ","handover":{"available":true,"target":"work_order","mode":"manual","work_order":{"id":"01a1c0b2-7d4e-7a19-8c35-2f9e6b1d4a07","order_number":"AO-10483","status":"booked"},"invoice":null,"blocked_reason":null},"lines":[{"id":"01a1bf92-5e1a-7c40-8f2b-9d3c6a1e7b05","product_id":null,"name":"Montering","description":null,"quantity":8,"unit_price_cents":50000,"discount_percent":0,"vat_rate":25,"line_total_cents":400000,"net_cents":400000,"vat_cents":100000,"line_type":"labour","is_optional":false,"is_selected":true,"billing_cycle":null,"position":0},{"id":"01a1bf93-0b7c-7d21-a4e8-1f6b2c9d3a70","product_id":null,"name":"Fönster","description":null,"quantity":2,"unit_price_cents":400000,"discount_percent":0,"vat_rate":25,"line_total_cents":800000,"net_cents":800000,"vat_cents":200000,"line_type":"material","is_optional":false,"is_selected":true,"billing_cycle":null,"position":1}],"created_at":"2026-10-13T08:30:00+00:00","updated_at":"2026-10-18T08:41:12+00:00"}}}}},"200":{"description":"The quote had already been handed over — by acceptance or an earlier call — and nothing new was made. `handover` points at what it became.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteEnvelope"},"example":{"data":{"id":"01a1bf90-2c6a-7e45-9d18-4b7f3a0e6c21","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":null,"quote_number":"Q-2026-0142","title":"Fönsterbyte Solna – offert","status":"accepted","subtotal_cents":1200000,"discount_cents":0,"discount_percent":0,"tax_cents":300000,"tax_percent":25,"total_cents":1500000,"vat_breakdown":[{"rate":25,"base_cents":1200000,"vat_cents":300000}],"rot_rut":{"type":"none","rate_percent":null,"labour_cents":0,"deduction_cents":0,"customer_pays_cents":1500000,"preliminary":true,"annual_ceiling_applied":false},"currency":"SEK","expires_at":"2026-11-15","accepted_at":"2026-10-18T08:41:12+00:00","accepted_by_stakeholder_id":null,"acceptance":{"via":"link","name":"Eva Ek","email":"eva.ek@example.se","selected_optional_line_ids":[],"total_cents":1500000,"tax_cents":300000,"customer_pays_cents":1500000},"rejection_reason":null,"share_url":"https://app.closepact.com/q/k3Jq8vR2mXw9TzL5pN7cH4bY6sD1fG0aE8uQ3iO5rV2tW9xZ","handover":{"available":true,"target":"invoice","mode":"automatic","work_order":null,"invoice":{"id":"01a1c1e8-5a3d-7b62-9f40-8c2e1d7b0a95","status":"draft","reference":"Utkast"},"blocked_reason":null},"lines":[],"created_at":"2026-10-13T08:30:00+00:00","updated_at":"2026-10-18T08:41:12+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/QuoteConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/quotes/{quote}/lines":{"parameters":[{"$ref":"#/components/parameters/QuoteId"}],"post":{"operationId":"addQuoteLine","tags":["Quotes"],"summary":"Add a line","description":"Appends a line to the quote and recomputes its totals. Answers with the whole quote.\n\nWith `product_id`, the line starts from that catalogue product: its name, description, unit price, VAT rate and billing cycle are copied onto the line (a snapshot — later changes to the product do not reach the quote), and anything you send overrides them. Without it, `name` is required, the price defaults to 0 and the VAT rate to 25.\n\nAn optional line (`is_optional: true`) is priced like any other but counts in the totals only while `is_selected`; it starts unticked unless you send `is_selected: true` to suggest it. The buyer makes the final choice when accepting.\n\nOnly a draft's lines change: on a sent or expired quote this answers 409 `quote_not_draft` (revert it to draft first), on an accepted or rejected one 409 `quote_locked`.\n\nNot idempotent: each call adds another line.","x-scope":"write","x-module":"proposals","x-feature":"proposals.cpq","x-permissions":["cpq.quotes.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["quantity"],"properties":{"product_id":{"type":["string","null"],"format":"uuid","description":"A product in this organization's catalogue."},"name":{"type":["string","null"],"maxLength":255,"description":"Required without `product_id`."},"description":{"type":["string","null"],"maxLength":2000},"quantity":{"type":"number","minimum":0.001,"maximum":100000,"description":"Up to three decimals: 2.5 hours, 13.75 m²."},"unit_price_cents":{"type":["integer","null"],"minimum":0,"description":"Price per unit in öre, excluding VAT. Defaults to the product's price, or 0."},"discount_percent":{"type":["integer","null"],"minimum":0,"maximum":100,"default":0},"vat_rate":{"type":["number","null"],"enum":[25,12,6,0,null],"description":"Moms in percent. Defaults to the product's rate, or 25."},"line_type":{"type":["string","null"],"enum":["labour","material","other",null],"default":"other","description":"Only `labour` counts towards a ROT/RUT deduction."},"is_optional":{"type":["boolean","null"],"default":false,"description":"An option the buyer may tick in or out."},"is_selected":{"type":["boolean","null"],"default":false,"description":"For an optional line: counted in the totals until the buyer decides. Ignored for a fixed line, which always counts."},"billing_cycle":{"type":["string","null"],"enum":["monthly","yearly",null],"description":"For recurring lines. Defaults to the product's."}},"additionalProperties":false},"examples":{"fromProduct":{"summary":"From the catalogue","value":{"product_id":"01a15b70-6c7d-7e8f-9a0b-1c2d3e4f5a6b","quantity":2,"line_type":"labour"}},"freeText":{"summary":"Free text","value":{"name":"Storstädning vid start","quantity":1,"unit_price_cents":250000,"discount_percent":20}},"optional":{"summary":"An option the buyer can tick","value":{"name":"Fönsterputs","quantity":1.5,"unit_price_cents":45000,"vat_rate":25,"line_type":"labour","is_optional":true}}}}}},"responses":{"201":{"description":"The quote with the new line and recomputed totals.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteEnvelope"},"example":{"data":{"id":"01a15c50-8d9e-7f0a-b1c2-d3e4f5a6b7c8","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","quote_number":"Q-00042","title":"Offert hemstädning – familjen Ny","status":"draft","subtotal_cents":320000,"discount_cents":0,"discount_percent":0,"tax_cents":80000,"tax_percent":25,"total_cents":400000,"vat_breakdown":[{"rate":25,"base_cents":320000,"vat_cents":80000}],"rot_rut":{"type":"rut","rate_percent":50,"labour_cents":400000,"deduction_cents":200000,"customer_pays_cents":200000,"preliminary":true,"annual_ceiling_applied":false},"currency":"SEK","expires_at":"2026-11-17","accepted_at":null,"accepted_by_stakeholder_id":null,"acceptance":null,"rejection_reason":null,"share_url":null,"handover":{"available":true,"target":"work_order","mode":"automatic","work_order":null,"invoice":null,"blocked_reason":null},"lines":[{"id":"01a15c52-1a2b-7c3d-8e4f-5a6b7c8d9e0f","product_id":"01a15b70-6c7d-7e8f-9a0b-1c2d3e4f5a6b","name":"Hemstäd varannan vecka","description":null,"quantity":2,"unit_price_cents":160000,"discount_percent":0,"vat_rate":25,"line_total_cents":320000,"net_cents":320000,"vat_cents":80000,"line_type":"labour","is_optional":false,"is_selected":true,"billing_cycle":"monthly","position":0}],"created_at":"2026-10-13T08:30:00+00:00","updated_at":"2026-10-13T08:41:57+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/QuoteConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/quotes/{quote}/lines/{line}":{"parameters":[{"$ref":"#/components/parameters/QuoteId"},{"name":"line","in":"path","required":true,"description":"The line's id. It must belong to this quote.","schema":{"type":"string","format":"uuid"}}],"patch":{"operationId":"updateQuoteLine","tags":["Quotes"],"summary":"Update a line","description":"Changes the fields you send and recomputes the quote's totals. Answers with the whole quote. A field sent as `null` is left as it is, so a line's description or billing cycle cannot be cleared here — replace the line instead. The product a line came from cannot be changed. Only a draft's lines change (409 otherwise, as for adding one).","x-scope":"write","x-module":"proposals","x-feature":"proposals.cpq","x-permissions":["cpq.quotes.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":["string","null"],"maxLength":255},"description":{"type":["string","null"],"maxLength":2000},"quantity":{"type":["number","null"],"minimum":0.001,"maximum":100000,"description":"Up to three decimals."},"unit_price_cents":{"type":["integer","null"],"minimum":0},"discount_percent":{"type":["integer","null"],"minimum":0,"maximum":100},"vat_rate":{"type":["number","null"],"enum":[25,12,6,0,null]},"line_type":{"type":["string","null"],"enum":["labour","material","other",null]},"is_optional":{"type":["boolean","null"],"description":"Making a line fixed counts it again; making it optional leaves it unticked unless `is_selected` says otherwise."},"is_selected":{"type":["boolean","null"],"description":"For an optional line only."},"billing_cycle":{"type":["string","null"],"enum":["monthly","yearly",null]}},"additionalProperties":false},"example":{"quantity":4,"discount_percent":5}}}},"responses":{"200":{"description":"The quote with the changed line and recomputed totals.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteEnvelope"},"example":{"data":{"id":"01a15c50-8d9e-7f0a-b1c2-d3e4f5a6b7c8","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","deal_room_id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","quote_number":"Q-00042","title":"Offert hemstädning – familjen Ny","status":"draft","subtotal_cents":608000,"discount_cents":0,"discount_percent":0,"tax_cents":152000,"tax_percent":25,"total_cents":760000,"vat_breakdown":[{"rate":25,"base_cents":608000,"vat_cents":152000}],"rot_rut":{"type":"rut","rate_percent":50,"labour_cents":760000,"deduction_cents":380000,"customer_pays_cents":380000,"preliminary":true,"annual_ceiling_applied":false},"currency":"SEK","expires_at":"2026-11-17","accepted_at":null,"accepted_by_stakeholder_id":null,"acceptance":null,"rejection_reason":null,"share_url":null,"handover":{"available":true,"target":"work_order","mode":"automatic","work_order":null,"invoice":null,"blocked_reason":null},"lines":[{"id":"01a15c52-1a2b-7c3d-8e4f-5a6b7c8d9e0f","product_id":"01a15b70-6c7d-7e8f-9a0b-1c2d3e4f5a6b","name":"Hemstäd varannan vecka","description":null,"quantity":4,"unit_price_cents":160000,"discount_percent":5,"vat_rate":25,"line_total_cents":608000,"net_cents":608000,"vat_cents":152000,"line_type":"labour","is_optional":false,"is_selected":true,"billing_cycle":"monthly","position":0}],"created_at":"2026-10-13T08:30:00+00:00","updated_at":"2026-10-13T08:47:03+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/QuoteConflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteQuoteLine","tags":["Quotes"],"summary":"Delete a line","description":"Removes the line and recomputes the quote's totals. The other lines keep their positions. Only a draft's lines change (409 otherwise, as for adding one).","x-scope":"write","x-module":"proposals","x-feature":"proposals.cpq","x-permissions":["cpq.quotes.create"],"responses":{"204":{"description":"The line was deleted. No body."},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/QuoteConflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/recurrences":{"get":{"operationId":"listRecurrences","tags":["Recurrences"],"summary":"List recurrences","description":"Standing agreements ordered by start date. `due_before` answers \"which agreements produce work before this date\" from `next_run_on`. Cursor-paginated: pass `next_cursor` as `cursor`.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"q","in":"query","required":false,"description":"Search in the customer name (case-insensitive, substring).","schema":{"type":"string"},"example":"Linden"},{"name":"filter[status]","in":"query","required":false,"description":"Only these statuses. Repeat as `filter[status][]=active&filter[status][]=paused` for several.","schema":{"type":"string","enum":["active","paused","completed"],"description":"`active` agreements write jobs; `paused` ones wait (a customer away for the summer); `completed` ones have run out — set by the generator when the rule produces no more dates, or by you."},"example":"active"},{"name":"filter[account_id]","in":"query","required":false,"description":"Only this customer's agreements.","schema":{"type":"string","format":"uuid"}},{"name":"due_before","in":"query","required":false,"description":"Only agreements whose `next_run_on` is on or before this date. Calendar date, `YYYY-MM-DD`.","schema":{"type":"string","format":"date"},"example":"2026-12-01"},{"name":"limit","in":"query","required":false,"description":"Items per page, 1–200.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of recurrences.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BookingCursorPage"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Recurrence"}}}}]},"example":{"data":[{"id":"01a15c30-6f70-7364-b5c6-d7e82f354189","sub_entity_id":null,"account_id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","customer":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":"10231"},"rrule":"FREQ=WEEKLY;INTERVAL=2;BYDAY=MO","template":{"title":"Trappstädning – Brf Linden, Storgatan 12","service_address_id":"01a15c02-4e1c-7d5e-9f40-7182c9daeb23","price_model":"fixed","agreed_price_cents":145000,"start_time":"06:30","duration_minutes":120},"starts_on":"2026-11-02","ends_on":"2027-06-28","next_run_on":"2026-11-30","status":"active","lead_time_days":21,"last_generated_at":"2026-11-02T03:00:12+00:00","created_at":"2026-10-20T10:12:00+00:00","updated_at":"2026-11-02T03:00:12+00:00"}],"path":"https://api.closepact.com/v1/api/recurrences","per_page":50,"next_cursor":null,"next_page_url":null,"prev_cursor":null,"prev_page_url":null}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createRecurrence","tags":["Recurrences"],"summary":"Create a recurrence","description":"Creates a standing agreement. Every night Closepact writes a work order (source `recurrence`, status `booked`, unassigned) for each date the rule produces from today up to `lead_time_days` ahead — never for dates in the past, and never twice for the same date. To see the jobs at once rather than tomorrow, call `POST /recurrences/{recurrence}/generate`.\n\nThe rule is checked when saved: an unsupported part is a 422 that names it. `template.title` is required. Ids in the body and the template must be your organization's.","x-scope":"write","x-module":"booking","x-feature":"booking.recurring","x-permissions":["booking.work_orders.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["account_id","rrule","template","starts_on"],"properties":{"account_id":{"type":"string","format":"uuid","description":"The customer. Must be one of your organization's."},"sub_entity_id":{"type":["string","null"],"format":"uuid","description":"The office (sub-entity) it belongs to. Must be one of yours."},"rrule":{"type":"string","maxLength":500,"description":"The rule, e.g. `FREQ=WEEKLY;INTERVAL=2;BYDAY=MO`. A leading `RRULE:` is accepted. Supported: `FREQ=DAILY|WEEKLY|MONTHLY`, `INTERVAL`, `BYDAY` (`MO`…`SU`), `COUNT`, `UNTIL` (not both). Anything else is a 422 naming the part."},"template":{"$ref":"#/components/schemas/RecurrenceTemplate"},"starts_on":{"type":"string","format":"date","description":"First day. Calendar date, `YYYY-MM-DD`."},"ends_on":{"type":["string","null"],"format":"date","description":"Last day, or null; not before `starts_on`. Calendar date, `YYYY-MM-DD`."},"lead_time_days":{"type":"integer","description":"How far ahead to write jobs, 1–120 days. Default 14.","minimum":1,"maximum":120},"status":{"type":"string","enum":["active","paused","completed"],"description":"`active` agreements write jobs; `paused` ones wait (a customer away for the summer); `completed` ones have run out — set by the generator when the rule produces no more dates, or by you."}},"additionalProperties":false},"example":{"account_id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","rrule":"FREQ=WEEKLY;INTERVAL=2;BYDAY=MO","starts_on":"2026-11-02","ends_on":"2027-06-28","lead_time_days":21,"template":{"title":"Trappstädning – Brf Linden, Storgatan 12","service_address_id":"01a15c02-4e1c-7d5e-9f40-7182c9daeb23","price_model":"fixed","agreed_price_cents":145000,"start_time":"06:30","duration_minutes":120}}}}},"responses":{"201":{"description":"The recurrence. `next_run_on` is the first date the rule produces.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Recurrence"}},"additionalProperties":false},"example":{"data":{"id":"01a15c30-6f70-7364-b5c6-d7e82f354189","sub_entity_id":null,"account_id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","customer":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":"10231"},"rrule":"FREQ=WEEKLY;INTERVAL=2;BYDAY=MO","template":{"title":"Trappstädning – Brf Linden, Storgatan 12","service_address_id":"01a15c02-4e1c-7d5e-9f40-7182c9daeb23","price_model":"fixed","agreed_price_cents":145000,"start_time":"06:30","duration_minutes":120},"starts_on":"2026-11-02","ends_on":"2027-06-28","next_run_on":"2026-11-02","status":"active","lead_time_days":21,"last_generated_at":null,"created_at":"2026-10-20T10:12:00+00:00","updated_at":"2026-10-20T10:12:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/recurrences/{recurrence}":{"get":{"operationId":"getRecurrence","tags":["Recurrences"],"summary":"Get a recurrence","description":"One standing agreement. The jobs it has written are work orders with `source` `recurrence`.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"recurrence","in":"path","required":true,"description":"The recurrence id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The recurrence.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Recurrence"}},"additionalProperties":false},"example":{"data":{"id":"01a15c30-6f70-7364-b5c6-d7e82f354189","sub_entity_id":null,"account_id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","customer":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":"10231"},"rrule":"FREQ=WEEKLY;INTERVAL=2;BYDAY=MO","template":{"title":"Trappstädning – Brf Linden, Storgatan 12","service_address_id":"01a15c02-4e1c-7d5e-9f40-7182c9daeb23","price_model":"fixed","agreed_price_cents":145000,"start_time":"06:30","duration_minutes":120},"starts_on":"2026-11-02","ends_on":"2027-06-28","next_run_on":"2026-11-30","status":"active","lead_time_days":21,"last_generated_at":"2026-11-02T03:00:12+00:00","created_at":"2026-10-20T10:12:00+00:00","updated_at":"2026-11-02T03:00:12+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateRecurrence","tags":["Recurrences"],"summary":"Update a recurrence","description":"Changes the agreement. `template` is merged into the stored template: send only the keys to change, and a key set to `null` to remove it. Changing `rrule`, `starts_on` or `ends_on` recomputes `next_run_on`.\n\nJobs already written are left alone — they may be assigned or already worked on. Move or cancel the ones the change makes wrong; pause the agreement with `status: paused`.","x-scope":"write","x-module":"booking","x-feature":"booking.recurring","x-permissions":["booking.work_orders.manage"],"parameters":[{"name":"recurrence","in":"path","required":true,"description":"The recurrence id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"account_id":{"type":"string","format":"uuid","description":"The customer. Must be one of your organization's."},"sub_entity_id":{"type":["string","null"],"format":"uuid","description":"The office (sub-entity) it belongs to. Must be one of yours."},"rrule":{"type":"string","maxLength":500,"description":"The rule, e.g. `FREQ=WEEKLY;INTERVAL=2;BYDAY=MO`. A leading `RRULE:` is accepted. Supported: `FREQ=DAILY|WEEKLY|MONTHLY`, `INTERVAL`, `BYDAY` (`MO`…`SU`), `COUNT`, `UNTIL` (not both). Anything else is a 422 naming the part."},"starts_on":{"type":"string","format":"date","description":"First day. Calendar date, `YYYY-MM-DD`."},"ends_on":{"type":["string","null"],"format":"date","description":"Last day, or null; not before `starts_on`. Calendar date, `YYYY-MM-DD`."},"lead_time_days":{"type":"integer","description":"How far ahead to write jobs, 1–120 days. Default 14.","minimum":1,"maximum":120},"status":{"type":"string","enum":["active","paused","completed"],"description":"`active` agreements write jobs; `paused` ones wait (a customer away for the summer); `completed` ones have run out — set by the generator when the rule produces no more dates, or by you."},"template":{"type":"object","description":"Keys to change in the template; the others are kept. Sending `null` removes a key that may be empty (`description`, `contact_id`, `service_address_id`, `agreed_price_cents`, `rot_rut_share`, `estimated_minutes`). The result must still have a `title`.","properties":{"title":{"type":["string","null"],"description":"Title of each job. Cannot be emptied.","maxLength":255},"description":{"type":["string","null"],"description":"Description of each job."},"contact_id":{"type":["string","null"],"format":"uuid","description":"Contact at the customer."},"service_address_id":{"type":["string","null"],"format":"uuid","description":"Where the work is done: one of the customer's addresses."},"priority":{"type":"integer","description":"-2 to 2.","minimum":-2,"maximum":2},"price_model":{"type":"string","enum":["hourly","fixed","per_unit"],"description":"How each job is charged."},"agreed_price_cents":{"type":["integer","null"],"description":"Price per visit for a `fixed` job, in öre excluding VAT.","minimum":0},"rot_rut":{"type":"string","enum":["none","rot","rut"],"description":"ROT/RUT deduction on each job."},"rot_rut_share":{"type":["number","null"],"description":"Share of the deduction, 0–100 percent.","minimum":0,"maximum":100},"estimated_minutes":{"type":["integer","null"],"description":"Expected duration in minutes.","minimum":1},"start_time":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Time of day each job starts, `HH:MM`, on the organization's local clock (Swedish time, `Europe/Stockholm`): `06:30` is 06:30 in Stockholm all year, so the job's `planned_start` is 05:30 UTC in winter and 04:30 UTC in summer. Converted per occurrence, so it stays put across daylight-saving changes. Default `08:00`."},"duration_minutes":{"type":"integer","description":"Planned length of each visit, 1–1440 minutes; sets `planned_end`. Without it (or `estimated_minutes`) the jobs have no planned end.","minimum":1,"maximum":1440}},"additionalProperties":false}},"additionalProperties":false},"example":{"status":"paused","template":{"start_time":"07:00"}}}}},"responses":{"200":{"description":"The recurrence.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Recurrence"}},"additionalProperties":false},"example":{"data":{"id":"01a15c30-6f70-7364-b5c6-d7e82f354189","sub_entity_id":null,"account_id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","customer":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":"10231"},"rrule":"FREQ=WEEKLY;INTERVAL=2;BYDAY=MO","template":{"title":"Trappstädning – Brf Linden, Storgatan 12","service_address_id":"01a15c02-4e1c-7d5e-9f40-7182c9daeb23","price_model":"fixed","agreed_price_cents":145000,"start_time":"07:00","duration_minutes":120},"starts_on":"2026-11-02","ends_on":"2027-06-28","next_run_on":"2026-11-30","status":"paused","lead_time_days":21,"last_generated_at":"2026-11-02T03:00:12+00:00","created_at":"2026-10-20T10:12:00+00:00","updated_at":"2026-11-10T08:00:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteRecurrence","tags":["Recurrences"],"summary":"Delete a recurrence","description":"Deletes the agreement. The jobs it wrote are kept, so work done under it can still be invoiced; they simply stop pointing at it. To stop an agreement for a while, pause it instead.","x-scope":"write","x-module":"booking","x-feature":"booking.recurring","x-permissions":["booking.work_orders.manage"],"parameters":[{"name":"recurrence","in":"path","required":true,"description":"The recurrence id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The recurrence was deleted.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingDeleted"},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/recurrences/{recurrence}/generate":{"post":{"operationId":"generateRecurrence","tags":["Recurrences"],"summary":"Generate jobs now","description":"Runs the nightly generator for this agreement now: writes a work order for every date from today to the horizon (today + `lead_time_days`, or `ends_on` if earlier) that does not have one yet, at most 200 per run. Safe to repeat — dates that already have a job, including ones deleted on purpose, are counted as `skipped`, never written twice.\n\nWith `dry_run=true` nothing is written: `dates` shows what would be considered and `created` is 0. A run updates `last_generated_at` and `next_run_on`, and marks the agreement `completed` when its rule has no dates left. Only an `active` agreement can be generated (409 otherwise).","x-scope":"write","x-module":"booking","x-feature":"booking.recurring","x-permissions":["booking.work_orders.manage"],"parameters":[{"name":"recurrence","in":"path","required":true,"description":"The recurrence id.","schema":{"type":"string","format":"uuid"}},{"name":"dry_run","in":"query","required":false,"description":"`true` to see the dates without writing anything. May also be sent in the body.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"What the run did.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/RecurrenceRun"}},"additionalProperties":false},"example":{"data":{"created":2,"skipped":0,"horizon":"2026-11-23","dates":["2026-11-02","2026-11-16"],"work_orders":[{"id":"01a15c31-7081-7475-86d7-e8f9304652a1","order_number":"1051","title":"Trappstädning – Brf Linden, Storgatan 12","occurrence_date":"2026-11-02","planned_start":"2026-11-02T05:30:00+00:00"},{"id":"01a15c31-7082-7586-97e8-f90a415763b2","order_number":"1052","title":"Trappstädning – Brf Linden, Storgatan 12","occurrence_date":"2026-11-16","planned_start":"2026-11-16T05:30:00+00:00"}],"recurrence":{"id":"01a15c30-6f70-7364-b5c6-d7e82f354189","sub_entity_id":null,"account_id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","customer":null,"rrule":"FREQ=WEEKLY;INTERVAL=2;BYDAY=MO","template":{"title":"Trappstädning – Brf Linden, Storgatan 12","service_address_id":"01a15c02-4e1c-7d5e-9f40-7182c9daeb23","price_model":"fixed","agreed_price_cents":145000,"start_time":"06:30","duration_minutes":120},"starts_on":"2026-11-02","ends_on":"2027-06-28","next_run_on":"2026-11-30","status":"active","lead_time_days":21,"last_generated_at":"2026-11-02T08:14:00+00:00","created_at":"2026-10-20T10:12:00+00:00","updated_at":"2026-11-02T08:14:00+00:00"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The agreement is paused or completed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConflictError"},"example":{"message":"A paused agreement does not produce work. Make it active first.","error":"conflict"}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms":{"get":{"operationId":"listRooms","tags":["Rooms"],"summary":"List rooms","description":"Returns the organization's rooms as a cursor-paginated list, newest first unless you ask for another order. Rooms in the list carry no `contents` or `stakeholders`; fetch a single room for those.\n\nTo match several statuses, repeat the filter with brackets: `filter[status][]=draft&filter[status][]=published`. Page through with `cursor` until `meta.next_cursor` is `null`.","x-scope":"read","x-module":"proposals","x-permissions":[],"parameters":[{"name":"filter[status]","in":"query","required":false,"description":"Only rooms with this status.","schema":{"$ref":"#/components/schemas/RoomStatus"}},{"name":"filter[deal_id]","in":"query","required":false,"description":"Only rooms for this deal.","schema":{"type":"string","format":"uuid"}},{"name":"q","in":"query","required":false,"description":"Text the room title contains, case-insensitively.","schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"description":"One sort field, optionally prefixed with `-` for descending. Unpublished rooms sort as though published at the end of time when sorting by `published_at`.","schema":{"type":"string","enum":["created_at","-created_at","updated_at","-updated_at","title","-title","published_at","-published_at"],"default":"-created_at"}},{"$ref":"#/components/parameters/SalesLimit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of rooms.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Room"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","slug":"k7Qm2xTb9pLw","title":"Hemstäd för familjen Ny","description":"Allt om er städning: offert, avtal och schema.","status":"published","published_at":"2026-10-13T09:00:00+00:00","expires_at":"2026-11-13T09:00:00+00:00","require_email":true,"password_protected":false,"brand_overrides":null,"settings":null,"deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","created_by":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se"},"created_at":"2026-10-12T08:20:11+00:00","updated_at":"2026-10-13T09:00:00+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":null},"meta":{"path":"https://api.closepact.com/v1/api/rooms","per_page":25,"next_cursor":null,"prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createRoom","tags":["Rooms"],"summary":"Create a room","description":"Creates an empty room as a draft, with a fresh unguessable `slug` and the member the key acts as as its creator. Buyers cannot open it until it is published.\n\nA typical integration creates the room, adds contents and stakeholders, then publishes it. Not idempotent: each call creates another room.","x-scope":"write","x-module":"proposals","x-feature":"proposals.rooms","x-permissions":["rooms.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title"],"properties":{"title":{"type":"string","minLength":2,"maxLength":255},"description":{"type":["string","null"],"maxLength":5000,"description":"Shown to buyers under the title."},"deal_id":{"type":["string","null"],"format":"uuid","description":"A deal in this organization. Cannot be changed later."},"expires_at":{"type":["string","null"],"format":"date-time","description":"When buyers lose access. Must be in the future."},"require_email":{"type":"boolean","default":true,"description":"Ask visitors for their e-mail before showing the room."},"brand_overrides":{"type":["object","null"],"additionalProperties":true,"description":"Colours, logo and fonts for this room instead of the organization's brand kit."},"settings":{"type":["object","null"],"additionalProperties":true,"description":"Display settings used by the room page."}},"additionalProperties":false},"example":{"title":"Hemstäd för familjen Ny","description":"Allt om er städning: offert, avtal och schema.","deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","expires_at":"2026-11-13T09:00:00+00:00"}}}},"responses":{"201":{"description":"The room, as created.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomEnvelope"},"example":{"data":{"id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","slug":"k7Qm2xTb9pLw","title":"Hemstäd för familjen Ny","description":"Allt om er städning: offert, avtal och schema.","status":"draft","published_at":null,"expires_at":"2026-11-13T09:00:00+00:00","require_email":true,"password_protected":false,"brand_overrides":null,"settings":null,"deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","created_by":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se"},"created_at":"2026-10-12T08:20:11+00:00","updated_at":"2026-10-12T08:20:11+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{room}":{"parameters":[{"$ref":"#/components/parameters/RoomId"}],"get":{"operationId":"getRoom","tags":["Rooms"],"summary":"Retrieve a room","description":"Returns one room with its contents (in display order) and its stakeholders.","x-scope":"read","x-module":"proposals","x-permissions":[],"responses":{"200":{"description":"The room.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomEnvelope"},"example":{"data":{"id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","slug":"k7Qm2xTb9pLw","title":"Hemstäd för familjen Ny","description":"Allt om er städning: offert, avtal och schema.","status":"published","published_at":"2026-10-13T09:00:00+00:00","expires_at":"2026-11-13T09:00:00+00:00","require_email":true,"password_protected":false,"brand_overrides":null,"settings":null,"deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","created_by":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se"},"contents":[{"id":"01a15c44-7a1b-7c2d-9e3f-0a1b2c3d4e5f","section":"overview","kind":"text","title":"Välkommen","body":"Här hittar ni allt om er hemstädning varannan vecka.","url":null,"payload":null,"position":1,"content_type":null,"content_id":null}],"stakeholders":[{"id":"01a15c47-2b3c-7d4e-8f5a-6b7c8d9e0f1a","email":"henrik.ny@solglantan.se","name":"Henrik Ny","role":"economic","status":"active","engagement_score":34,"influence_score":0,"last_active_at":"2026-10-14T18:22:05+00:00","invited_at":"2026-10-13T09:01:30+00:00"}],"created_at":"2026-10-12T08:20:11+00:00","updated_at":"2026-10-13T09:00:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateRoom","tags":["Rooms"],"summary":"Update a room","description":"Changes the fields you send and leaves the rest alone. Changes are visible to buyers at once if the room is published.\n\nSet `password` to require it before the room opens, or `null` to remove it; the password is stored hashed and never returned — `password_protected` tells you whether one is set. `expires_at` may be moved, cleared or set in the past here. The status, slug and deal cannot be changed: use [Publish a room](/developers/reference/rooms#publishRoom) and [Expire a room](/developers/reference/rooms#expireRoom).","x-scope":"write","x-module":"proposals","x-feature":"proposals.rooms","x-permissions":["rooms.update"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":2,"maxLength":255},"description":{"type":["string","null"],"maxLength":5000},"expires_at":{"type":["string","null"],"format":"date-time"},"require_email":{"type":"boolean"},"brand_overrides":{"type":["object","null"],"additionalProperties":true},"settings":{"type":["object","null"],"additionalProperties":true},"password":{"type":["string","null"],"minLength":6,"maxLength":128,"writeOnly":true,"description":"Password buyers must enter, or null to remove it."}},"additionalProperties":false},"example":{"title":"Hemstäd – familjen Ny","password":"solglantan2026"}}}},"responses":{"200":{"description":"The room, as it is now. Carries no `contents` or `stakeholders`.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomEnvelope"},"example":{"data":{"id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","slug":"k7Qm2xTb9pLw","title":"Hemstäd – familjen Ny","description":"Allt om er städning: offert, avtal och schema.","status":"draft","published_at":null,"expires_at":"2026-11-13T09:00:00+00:00","require_email":true,"password_protected":true,"brand_overrides":null,"settings":null,"deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","created_by":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se"},"created_at":"2026-10-12T08:20:11+00:00","updated_at":"2026-10-12T10:02:40+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteRoom","tags":["Rooms"],"summary":"Delete a room","description":"Deletes the room: it leaves lists and lookups at once, and a later request for it is a 404. Quotes and proposals pointing at it keep their `deal_room_id`.\n\nTo cut buyers off from a published room, [expire it](/developers/reference/rooms#expireRoom) before deleting it. No webhook fires.","x-scope":"write","x-module":"proposals","x-feature":"proposals.rooms","x-permissions":["rooms.delete"],"responses":{"200":{"description":"The room was deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesDeleted"},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{room}/publish":{"parameters":[{"$ref":"#/components/parameters/RoomId"}],"post":{"operationId":"publishRoom","tags":["Rooms"],"summary":"Publish a room","description":"Opens the room to buyers: `status` becomes `published` and `published_at` is set. Stakeholders' personal links and the room link start working, as long as `expires_at` is empty or in the future — publishing an expired room does not move its expiry, so clear or extend `expires_at` with an update first.\n\nFires the `room.published` webhook. Idempotent: publishing a published room changes nothing, keeps the original `published_at` and fires nothing. No request body.","x-scope":"write","x-module":"proposals","x-feature":"proposals.rooms","x-permissions":["rooms.publish"],"responses":{"200":{"description":"The published room. Carries no `contents` or `stakeholders`, and may carry no `created_by` when the room was already published.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomEnvelope"},"example":{"data":{"id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","slug":"k7Qm2xTb9pLw","title":"Hemstäd för familjen Ny","description":"Allt om er städning: offert, avtal och schema.","status":"published","published_at":"2026-10-13T09:00:00+00:00","expires_at":"2026-11-13T09:00:00+00:00","require_email":true,"password_protected":false,"brand_overrides":null,"settings":null,"deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","created_by":{"id":"01a14eb3-c634-7238-bc76-18b09b287f98","name":"Anna Lindqvist","email":"anna@nordiskfastighet.se"},"created_at":"2026-10-12T08:20:11+00:00","updated_at":"2026-10-13T09:00:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{room}/expire":{"parameters":[{"$ref":"#/components/parameters/RoomId"}],"post":{"operationId":"expireRoom","tags":["Rooms"],"summary":"Expire a room","description":"Closes the room to buyers now: `status` becomes `expired` and `expires_at` is set to the current time, so the room link and every stakeholder's personal link stop working at once. The room and everything in it are kept; publish it again (after moving `expires_at`) to reopen it.\n\nEach call stamps `expires_at` anew. No webhook fires. No request body.","x-scope":"write","x-module":"proposals","x-feature":"proposals.rooms","x-permissions":["rooms.publish"],"responses":{"200":{"description":"The expired room. Carries no `created_by`, `contents` or `stakeholders`.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomEnvelope"},"example":{"data":{"id":"01a15c40-1f2e-7d9a-8b3c-4e5f6a7b8c9d","slug":"k7Qm2xTb9pLw","title":"Hemstäd för familjen Ny","description":"Allt om er städning: offert, avtal och schema.","status":"expired","published_at":"2026-10-13T09:00:00+00:00","expires_at":"2026-10-20T16:45:00+00:00","require_email":true,"password_protected":false,"brand_overrides":null,"settings":null,"deal_id":"01a15c2e-4b7a-7f3e-9c1d-2e8f6a0b4c71","created_at":"2026-10-12T08:20:11+00:00","updated_at":"2026-10-20T16:45:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{room}/stakeholders":{"parameters":[{"$ref":"#/components/parameters/RoomId"}],"get":{"operationId":"listRoomStakeholders","tags":["Room stakeholders"],"summary":"List a room's stakeholders","description":"Returns every stakeholder in the room, with how engaged each one is. Not paginated.","x-scope":"read","x-module":"proposals","x-permissions":[],"responses":{"200":{"description":"The room's stakeholders.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Stakeholder"}}},"additionalProperties":false},"example":{"data":[{"id":"01a15c47-2b3c-7d4e-8f5a-6b7c8d9e0f1a","email":"henrik.ny@solglantan.se","name":"Henrik Ny","role":"economic","status":"active","engagement_score":34,"influence_score":0,"last_active_at":"2026-10-14T18:22:05+00:00","invited_at":"2026-10-13T09:01:30+00:00"},{"id":"01a15c48-9c0d-7e1f-a2b3-c4d5e6f7a8b9","email":"maria.ny@gmail.com","name":"Maria Ny","role":"champion","status":"invited","engagement_score":0,"influence_score":0,"last_active_at":null,"invited_at":"2026-10-13T09:02:10+00:00"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"addRoomStakeholder","tags":["Room stakeholders"],"summary":"Add a stakeholder","description":"Invites a person into the room by e-mail. A new stakeholder is sent an invitation e-mail with their personal link straight away — also while the room is still a draft, when the link will not open yet — and the `stakeholder.added` webhook fires.\n\nAn address that is already a stakeholder in this room (compared case-insensitively; addresses are stored lower case) is updated instead of duplicated: `name`, `role` and `contact_id` are replaced by what you send — left out means cleared, and `role` falls back to `user` — and `status` returns to `invited`. No e-mail or webhook is sent for an update, so the call is safe to repeat. Both cases answer 201.","x-scope":"write","x-module":"proposals","x-feature":"proposals.rooms","x-permissions":["rooms.update"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email","maxLength":255},"name":{"type":["string","null"],"maxLength":255},"role":{"$ref":"#/components/schemas/StakeholderRole"},"contact_id":{"type":["string","null"],"format":"uuid","description":"The contact in this organization this person is."}},"additionalProperties":false},"example":{"email":"henrik.ny@solglantan.se","name":"Henrik Ny","role":"economic","contact_id":"01a15d02-6e1f-7b44-a0c9-3d7e5f1b2a96"}}}},"responses":{"201":{"description":"The stakeholder, added or updated.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Stakeholder"}},"additionalProperties":false},"example":{"data":{"id":"01a15c47-2b3c-7d4e-8f5a-6b7c8d9e0f1a","email":"henrik.ny@solglantan.se","name":"Henrik Ny","role":"economic","status":"invited","engagement_score":0,"influence_score":0,"last_active_at":null,"invited_at":"2026-10-13T09:01:30+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{room}/stakeholders/{stakeholder}":{"parameters":[{"$ref":"#/components/parameters/RoomId"},{"name":"stakeholder","in":"path","required":true,"description":"The stakeholder's id. It must belong to this room.","schema":{"type":"string","format":"uuid"}}],"delete":{"operationId":"removeRoomStakeholder","tags":["Room stakeholders"],"summary":"Remove a stakeholder","description":"Removes the person from the room. Their personal links stop working at once; nobody is notified. Their engagement history is removed with them. Adding the same address again later invites them afresh, with a new e-mail.","x-scope":"write","x-module":"proposals","x-feature":"proposals.rooms","x-permissions":["rooms.update"],"responses":{"200":{"description":"The stakeholder was removed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesDeleted"},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{room}/contents":{"parameters":[{"$ref":"#/components/parameters/RoomId"}],"get":{"operationId":"listRoomContents","tags":["Room contents"],"summary":"List a room's contents","description":"Returns every content block in the room, ordered by `position`. Positions count up within each section, so group by `section` to rebuild the page. Not paginated.","x-scope":"read","x-module":"proposals","x-permissions":[],"responses":{"200":{"description":"The room's contents.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RoomContent"}}},"additionalProperties":false},"example":{"data":[{"id":"01a15c44-7a1b-7c2d-9e3f-0a1b2c3d4e5f","section":"overview","kind":"text","title":"Välkommen","body":"Här hittar ni allt om er hemstädning varannan vecka.","url":null,"payload":null,"position":1,"content_type":null,"content_id":null},{"id":"01a15c45-3d4e-7f5a-8b6c-7d8e9f0a1b2c","section":"documents","kind":"document","title":"Avtal hemstädning.pdf","body":null,"url":null,"payload":null,"position":1,"content_type":"document","content_id":"01a15c3f-5e6f-7a8b-9c0d-1e2f3a4b5c6d"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"addRoomContent","tags":["Room contents"],"summary":"Add content to a room","description":"Appends a content block to the end of a section. Which fields matter depends on `kind`: `text` uses `title` and `body`; `link` and `video` use `url`; `document`, `proposal` and `quote` point at a record of yours with `content_id` (and conventionally `content_type` set to the same word) — that record must belong to your organization. `section` blocks are headings.\n\n`visibility` restricts the block to some stakeholders; it is stored but not returned. Visible to buyers at once if the room is published. Not idempotent: each call adds another block.","x-scope":"write","x-module":"proposals","x-feature":"proposals.rooms","x-permissions":["rooms.update"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["section","kind"],"properties":{"section":{"$ref":"#/components/schemas/RoomContentSection"},"kind":{"$ref":"#/components/schemas/RoomContentKind"},"title":{"type":["string","null"],"maxLength":255},"body":{"type":["string","null"],"description":"Text for a `text` block."},"url":{"type":["string","null"],"format":"uri","maxLength":2000,"description":"For `link` and `video` blocks."},"content_type":{"type":["string","null"],"maxLength":32,"description":"What `content_id` points at: `document`, `proposal` or `quote`."},"content_id":{"type":["string","null"],"format":"uuid","description":"For `document`, `proposal` and `quote` blocks: the id of that record in your organization."},"payload":{"type":["object","null"],"additionalProperties":true,"description":"Extra data the room page renders the block with."},"visibility":{"type":["object","null"],"additionalProperties":true,"writeOnly":true,"description":"Which stakeholders may see the block."}},"additionalProperties":false},"example":{"section":"overview","kind":"text","title":"Välkommen","body":"Här hittar ni allt om er hemstädning varannan vecka."}}}},"responses":{"201":{"description":"The content block, as added.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/RoomContent"}},"additionalProperties":false},"example":{"data":{"id":"01a15c44-7a1b-7c2d-9e3f-0a1b2c3d4e5f","section":"overview","kind":"text","title":"Välkommen","body":"Här hittar ni allt om er hemstädning varannan vecka.","url":null,"payload":null,"position":1,"content_type":null,"content_id":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{room}/contents/{content}":{"parameters":[{"$ref":"#/components/parameters/RoomId"},{"name":"content","in":"path","required":true,"description":"The content block's id. It must belong to this room.","schema":{"type":"string","format":"uuid"}}],"delete":{"operationId":"removeRoomContent","tags":["Room contents"],"summary":"Remove content from a room","description":"Removes the block from the room. The document, proposal or quote it pointed at is not deleted. The other blocks keep their positions.","x-scope":"write","x-module":"proposals","x-feature":"proposals.rooms","x-permissions":["rooms.update"],"responses":{"200":{"description":"The block was removed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SalesDeleted"},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/services":{"get":{"operationId":"listServices","tags":["Services"],"summary":"List services","description":"The service catalogue by name, active and retired. Cursor-paginated: follow `links.next`, or pass `meta.next_cursor` as `cursor`.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"q","in":"query","required":false,"description":"Search in the name (case-insensitive, substring).","schema":{"type":"string"},"example":"städ"},{"name":"filter[active]","in":"query","required":false,"description":"`1` for only active services, `0` for only retired ones.","schema":{"type":"boolean"}},{"name":"filter[skill_id]","in":"query","required":false,"description":"Only services that require this skill.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1–100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of services.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Service"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a15b80-5a6b-7c8d-9eaf-b0c1d2e3f405","name":"Flyttstädning","description":"Städning enligt branschens checklista, inklusive ugn, kyl och fönster.","default_duration_min":240,"pricing":{"model":"hourly","unit_price_cents":49500,"unit_label":"timme","vat_rate":25,"rot_rut_eligible":true},"color":"#9B51E0","is_active":true,"sub_entity_id":null,"skills":[{"id":"01a15b70-7c8d-7e9f-a0b1-c2d3e4f50617","name":"Storstädning","color":"#27AE60"}],"created_at":"2025-11-04T09:00:00+00:00","updated_at":"2026-08-12T14:20:00+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":null},"meta":{"path":"https://api.closepact.com/v1/api/services","per_page":25,"next_cursor":null,"prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createService","tags":["Services"],"summary":"Create a service","description":"Adds a service to the catalogue. Each service carries its own VAT rate, because a cleaning is 25 % and a passenger transport 6 %.","x-scope":"write","x-module":"booking","x-permissions":["booking.resources.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"Name.","maxLength":160},"description":{"type":["string","null"],"description":"Description."},"default_duration_min":{"type":"integer","description":"Usual duration in minutes, 5–1440. Default 60.","minimum":5,"maximum":1440},"price_model":{"type":"string","enum":["hourly","fixed","per_unit"],"description":"Default `hourly`."},"unit_price_cents":{"type":"integer","description":"Amount in öre (1 SEK = 100 öre), excluding VAT. Default 0.","minimum":0},"unit_label":{"type":["string","null"],"description":"What a unit is called.","maxLength":32},"vat_rate":{"type":"integer","enum":[25,12,6,0],"description":"VAT in percent — one of the Swedish rates. Default 25."},"rot_rut_eligible":{"type":"boolean","description":"Whether the work qualifies for ROT/RUT. Default false."},"color":{"type":["string","null"],"description":"Colour on the board.","maxLength":16},"is_active":{"type":"boolean","description":"Default true."},"sub_entity_id":{"type":["string","null"],"format":"uuid","description":"The office (sub-entity) it belongs to. Must be one of yours."},"skill_ids":{"type":"array","description":"Replaces the required skills with these (see `GET /skills`). Ids that are not your organization's are ignored. Leave out to keep the current ones.","items":{"type":"string","format":"uuid"}}},"additionalProperties":false},"example":{"name":"Flyttstädning","description":"Städning enligt branschens checklista, inklusive ugn, kyl och fönster.","default_duration_min":240,"price_model":"hourly","unit_price_cents":49500,"unit_label":"timme","vat_rate":25,"rot_rut_eligible":true,"skill_ids":["01a15b70-7c8d-7e9f-a0b1-c2d3e4f50617"]}}}},"responses":{"201":{"description":"The service.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Service"}},"additionalProperties":false},"example":{"data":{"id":"01a15b80-5a6b-7c8d-9eaf-b0c1d2e3f405","name":"Flyttstädning","description":"Städning enligt branschens checklista, inklusive ugn, kyl och fönster.","default_duration_min":240,"pricing":{"model":"hourly","unit_price_cents":49500,"unit_label":"timme","vat_rate":25,"rot_rut_eligible":true},"color":"#9B51E0","is_active":true,"sub_entity_id":null,"skills":[{"id":"01a15b70-7c8d-7e9f-a0b1-c2d3e4f50617","name":"Storstädning","color":"#27AE60"}],"created_at":"2025-11-04T09:00:00+00:00","updated_at":"2026-08-12T14:20:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/services/{service}":{"get":{"operationId":"getService","tags":["Services"],"summary":"Get a service","description":"One service with its required skills.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"service","in":"path","required":true,"description":"The service id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The service.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Service"}},"additionalProperties":false},"example":{"data":{"id":"01a15b80-5a6b-7c8d-9eaf-b0c1d2e3f405","name":"Flyttstädning","description":"Städning enligt branschens checklista, inklusive ugn, kyl och fönster.","default_duration_min":240,"pricing":{"model":"hourly","unit_price_cents":49500,"unit_label":"timme","vat_rate":25,"rot_rut_eligible":true},"color":"#9B51E0","is_active":true,"sub_entity_id":null,"skills":[{"id":"01a15b70-7c8d-7e9f-a0b1-c2d3e4f50617","name":"Storstädning","color":"#27AE60"}],"created_at":"2025-11-04T09:00:00+00:00","updated_at":"2026-08-12T14:20:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateService","tags":["Services"],"summary":"Update a service","description":"Changes the fields you send. Prices already ordered on work orders do not change: they were copied when ordered.","x-scope":"write","x-module":"booking","x-permissions":["booking.resources.manage"],"parameters":[{"name":"service","in":"path","required":true,"description":"The service id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Name.","maxLength":160},"description":{"type":["string","null"],"description":"Description."},"default_duration_min":{"type":"integer","description":"Usual duration in minutes, 5–1440. Default 60.","minimum":5,"maximum":1440},"price_model":{"type":"string","enum":["hourly","fixed","per_unit"],"description":"Default `hourly`."},"unit_price_cents":{"type":"integer","description":"Amount in öre (1 SEK = 100 öre), excluding VAT. Default 0.","minimum":0},"unit_label":{"type":["string","null"],"description":"What a unit is called.","maxLength":32},"vat_rate":{"type":"integer","enum":[25,12,6,0],"description":"VAT in percent — one of the Swedish rates. Default 25."},"rot_rut_eligible":{"type":"boolean","description":"Whether the work qualifies for ROT/RUT. Default false."},"color":{"type":["string","null"],"description":"Colour on the board.","maxLength":16},"is_active":{"type":"boolean","description":"Default true."},"sub_entity_id":{"type":["string","null"],"format":"uuid","description":"The office (sub-entity) it belongs to. Must be one of yours."},"skill_ids":{"type":"array","description":"Replaces the required skills with these (see `GET /skills`). Ids that are not your organization's are ignored. Leave out to keep the current ones.","items":{"type":"string","format":"uuid"}}},"additionalProperties":false},"example":{"unit_price_cents":52500}}}},"responses":{"200":{"description":"The service.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Service"}},"additionalProperties":false},"example":{"data":{"id":"01a15b80-5a6b-7c8d-9eaf-b0c1d2e3f405","name":"Flyttstädning","description":"Städning enligt branschens checklista, inklusive ugn, kyl och fönster.","default_duration_min":240,"pricing":{"model":"hourly","unit_price_cents":52500,"unit_label":"timme","vat_rate":25,"rot_rut_eligible":true},"color":"#9B51E0","is_active":true,"sub_entity_id":null,"skills":[{"id":"01a15b70-7c8d-7e9f-a0b1-c2d3e4f50617","name":"Storstädning","color":"#27AE60"}],"created_at":"2025-11-04T09:00:00+00:00","updated_at":"2026-08-12T14:20:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteService","tags":["Services"],"summary":"Delete a service","description":"Removes the service from the catalogue. Work orders that ordered it keep their lines. To stop offering a service but keep it visible, set `is_active` to false instead.","x-scope":"write","x-module":"booking","x-permissions":["booking.resources.manage"],"parameters":[{"name":"service","in":"path","required":true,"description":"The service id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The service was deleted.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingDeleted"},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/signatures":{"get":{"operationId":"listSignatureRequests","tags":["Signature requests"],"summary":"List signature requests","description":"Every signature request in the organization, newest first, a page at a time, each with its document and signers. The audit trail (`events`) is only returned by `GET /signatures/{signature}`.\n\n`filter[status]` takes one value or several (`filter[status][]=sent&filter[status][]=partially_signed`).","x-scope":"read","x-module":null,"x-permissions":[],"parameters":[{"name":"filter[status]","in":"query","required":false,"description":"Only requests in this status. Repeat as `filter[status][]` for several.","schema":{"type":"string","enum":["draft","sent","partially_signed","completed","declined","expired","canceled"]}},{"name":"filter[deal_room_id]","in":"query","required":false,"description":"Only requests linked to this deal room.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Items per page. Values outside 1–100 are clamped.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of signature requests.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SignatureRequest"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a15a31-0c1f-7b90-a2e4-3d7c9b5f8e16","title":"Serviceavtal 2027 – Byggbolaget i Mälardalen AB","message":"Hej Erik! Här kommer serviceavtalet för 2027 enligt vårt samtal.","status":"partially_signed","signing_order":"sequential","sent_at":"2026-10-12T08:14:03+00:00","completed_at":null,"expires_at":"2026-11-11T22:59:59+00:00","canceled_at":null,"cancel_reason":null,"document":{"id":"01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47","title":"Serviceavtal 2027 – Byggbolaget i Mälardalen AB","mime_type":"application/pdf","size_bytes":284516},"signers":[{"id":"01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03","email":"erik.johansson@byggbolaget.se","name":"Erik Johansson","order_index":0,"status":"signed","opened_at":"2026-10-13T06:48:02+00:00","signed_at":"2026-10-13T06:52:44+00:00","declined_at":null,"decline_reason":null,"signature_type":"drawn"},{"id":"01a15a31-0c2e-71aa-9c3d-6b1e8f2a4d59","email":"anna@nordiskfastighet.se","name":"Anna Lindqvist","order_index":1,"status":"pending","opened_at":null,"signed_at":null,"declined_at":null,"decline_reason":null,"signature_type":null}],"deal_room_id":null,"created_at":"2026-10-12T08:14:03+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":null},"meta":{"path":"https://api.closepact.com/v1/api/signatures","per_page":25,"next_cursor":null,"prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createSignatureRequest","tags":["Signature requests"],"summary":"Create a signature request","description":"Creates a signature request for a document directly and emails every signer their link.\n\n**Prefer `POST /documents/{document}/send`.** This lower-level call differs in four ways: every person is a signer (there are no `cc` or `viewer` roles); it does not replace a request already out for the same document, so both stay open; it does not move the document's `lifecycle_status` to `sent` (it still becomes `signed` when this request completes); and it always returns each signer's link and token in `signer_links`.\n\n`signer_links` holds private credentials: anyone with a link or token can sign as that person. Deliver them only to their owner, and do not log them.\n\n`document_id` must be a document of this organization: an id that does not exist fails validation (`422`), and another organization's document answers `404`. The `signature.requested` webhook fires; when every signer has signed, the request becomes `completed` and `signature.completed` fires.","x-scope":"write","x-module":null,"x-permissions":["signatures.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["document_id","title","signers"],"properties":{"document_id":{"type":"string","format":"uuid"},"title":{"type":"string","minLength":2,"maxLength":255,"description":"Subject of the request, shown to signers."},"message":{"type":["string","null"],"maxLength":5000,"description":"A personal note in every signer's email."},"signing_order":{"type":"string","enum":["parallel","sequential"],"default":"parallel","description":"`sequential`: signers sign in the order listed."},"expires_at":{"type":["string","null"],"format":"date-time","description":"Signing deadline; must be in the future."},"deal_room_id":{"type":["string","null"],"format":"uuid","description":"Link the request to a deal room of this organization. Any other id, including another organization's room, fails validation (`422`)."},"signers":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"object","required":["email","name"],"properties":{"email":{"type":"string","format":"email"},"name":{"type":"string","maxLength":255},"stakeholder_id":{"type":["string","null"],"format":"uuid","description":"The deal-room stakeholder this signer is, if any: a stakeholder in one of this organization's rooms. Any other id fails validation (`422`)."},"phone_number":{"type":["string","null"],"maxLength":32,"description":"For the one-time code by SMS (see `signer_verification_channel` on the document). Stored as E.164."},"bankid_required":{"type":"boolean","default":false,"description":"This signer signs with BankID. `402` on a tier without BankID, `503` (`bankid_unavailable`) when BankID is not configured on the server."},"personal_number":{"type":["string","null"],"description":"The personnummer BankID has to report for this signer; anybody else's BankID is refused. Implies `bankid_required`. Never returned."}},"additionalProperties":false}}},"additionalProperties":false},"example":{"document_id":"01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47","title":"Serviceavtal 2027 – Byggbolaget i Mälardalen AB","message":"Hej Erik! Signera gärna avtalet före den 11 november.","signing_order":"sequential","expires_at":"2026-11-11T22:59:59+00:00","signers":[{"email":"erik.johansson@byggbolaget.se","name":"Erik Johansson"},{"email":"anna@nordiskfastighet.se","name":"Anna Lindqvist"}]}}}},"responses":{"201":{"description":"Created and sent.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","signer_links"],"properties":{"data":{"$ref":"#/components/schemas/SignatureRequest"},"signer_links":{"type":"array","description":"One per signer. Private: each link signs as that person.","items":{"$ref":"#/components/schemas/SignatureSignerLink"}}},"additionalProperties":false},"example":{"data":{"id":"01a15a31-0c1f-7b90-a2e4-3d7c9b5f8e16","title":"Serviceavtal 2027 – Byggbolaget i Mälardalen AB","message":"Hej Erik! Signera gärna avtalet före den 11 november.","status":"sent","signing_order":"sequential","sent_at":"2026-10-12T08:14:03+00:00","completed_at":null,"expires_at":"2026-11-11T22:59:59+00:00","canceled_at":null,"cancel_reason":null,"document":{"id":"01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47","title":"Serviceavtal 2027 – Byggbolaget i Mälardalen AB","mime_type":"application/pdf","size_bytes":284516},"signers":[{"id":"01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03","email":"erik.johansson@byggbolaget.se","name":"Erik Johansson","order_index":0,"status":"pending","opened_at":null,"signed_at":null,"declined_at":null,"decline_reason":null,"signature_type":null},{"id":"01a15a31-0c2e-71aa-9c3d-6b1e8f2a4d59","email":"anna@nordiskfastighet.se","name":"Anna Lindqvist","order_index":1,"status":"pending","opened_at":null,"signed_at":null,"declined_at":null,"decline_reason":null,"signature_type":null}],"deal_room_id":null,"created_at":"2026-10-12T08:14:03+00:00"},"signer_links":[{"signer_id":"01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03","email":"erik.johansson@byggbolaget.se","name":"Erik Johansson","token":"Xk3pQ9vB2mR7tY4wN8cL1zF6hJ0sD5gA2eK9uP3oW7iV4bM1","url":"https://app.closepact.com/sign/Xk3pQ9vB2mR7tY4wN8cL1zF6hJ0sD5gA2eK9uP3oW7iV4bM1"},{"signer_id":"01a15a31-0c2e-71aa-9c3d-6b1e8f2a4d59","email":"anna@nordiskfastighet.se","name":"Anna Lindqvist","token":"q7Wm2Lx9Rb4Tn8Vc1Pz6Hd3Kf0Gs5Ja2Ye7Uo9Ii4Ob1Na6M","url":"https://app.closepact.com/sign/q7Wm2Lx9Rb4Tn8Vc1Pz6Hd3Kf0Gs5Ja2Ye7Uo9Ii4Ob1Na6M"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/BankIdUnavailable"}}}},"/signatures/{signature}":{"parameters":[{"$ref":"#/components/parameters/SignatureRequestId"}],"get":{"operationId":"getSignatureRequest","tags":["Signature requests"],"summary":"Retrieve a signature request","description":"The request with its document, signers and its audit trail.\n\n**The audit trail** (`events`) is the evidence of signing, oldest first. Each event's `hash_self` is the SHA-256 of the event together with the previous event's hash (`hash_prev`), so the chain shows whether anything was altered or removed. Event types: `signature.requested` (with `payload.document_sha256`, the SHA-256 of the file sent), `recipient.added`, `recipient.removed`, `signer.opened`, `signer.code_sent` and `signer.verified` (a one-time code emailed to the signer and entered by them, with `payload.channel` and the masked `payload.destination`), `signer.signed` (with `payload.type`: `typed` or `drawn`, `payload.document_sha256` and `payload.verification`), `signer.declined` (with `payload.reason`), `signature.refused` (a signature refused because the document no longer matched, with `payload.reason`), `reminder.sent`, `signature.completed`, `signature.sealed` (the completion certificate was issued, with `payload.certificate_sha256` and the `payload.chain_head` it certifies), `signature.canceled` (with `payload.reason`). `ip_address` and `user_agent` are recorded for what recipients did.\n\n**Checking the chain.** `chain` is the server's own verification, recomputed on every request: `ok`, or `broken` with the first event that does not add up and why. Events written since October 2026 (`hash_version` 2) are numbered by `sequence` and hashed over the JSON of `{v, request_id, sequence, signer_id, event_type, payload, ip_address, user_agent, occurred_at, prev}` (keys of `payload` sorted, `occurred_at` in UTC as `YYYY-MM-DDTHH:MM:SSZ`), so you can recompute it yourself. Earlier events (`hash_version` 1, `sequence` null) are reported as `legacy`: each hash is checked, but their order rests on a one-second timestamp.\n\nThe `signature.completed` webhook carries this request's `id` — fetch it here when the webhook arrives, then download the file with `GET /documents/{document}/download-link`.","x-scope":"read","x-module":null,"x-permissions":[],"responses":{"200":{"description":"The signature request, with its audit trail.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SignatureRequest"}},"additionalProperties":false},"example":{"data":{"id":"01a15a31-0c1f-7b90-a2e4-3d7c9b5f8e16","title":"Serviceavtal 2027 – Byggbolaget i Mälardalen AB","message":null,"status":"completed","signing_order":"parallel","sent_at":"2026-10-12T08:14:03+00:00","completed_at":"2026-10-13T06:52:44+00:00","expires_at":"2026-11-11T22:59:59+00:00","canceled_at":null,"cancel_reason":null,"document":{"id":"01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47","title":"Serviceavtal 2027 – Byggbolaget i Mälardalen AB","mime_type":"application/pdf","size_bytes":284516},"signers":[{"id":"01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03","email":"erik.johansson@byggbolaget.se","name":"Erik Johansson","order_index":0,"status":"signed","opened_at":"2026-10-13T06:48:02+00:00","signed_at":"2026-10-13T06:52:44+00:00","declined_at":null,"decline_reason":null,"signature_type":"typed"}],"events":[{"id":"01a15a31-0c30-7d18-8e2f-4a6c9b1d3e57","signer_id":null,"sequence":1,"event_type":"signature.requested","payload":{"document_id":"01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47","signer_count":1},"hash_prev":null,"hash_self":"65472c7186549f263d194b44aad9908ccfbeed3ee1e33242957e9efe37096e4c","hash_version":2,"ip_address":null,"user_agent":null,"occurred_at":"2026-10-12T08:14:03+00:00"},{"id":"01a15b07-6e10-7a2b-8c4d-1e3f5a7b9c0d","signer_id":"01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03","sequence":2,"event_type":"signer.opened","payload":[],"hash_prev":"65472c7186549f263d194b44aad9908ccfbeed3ee1e33242957e9efe37096e4c","hash_self":"1d0e7c4b9a2f5e8d3c6b1a0f9e4d7c2b5a8f3e6d1c4b7a0e9f2d5c8b1a4e7f3d","hash_version":2,"ip_address":"81.230.44.17","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15","occurred_at":"2026-10-13T06:48:02+00:00"},{"id":"01a15b0b-2a41-7f6e-9d3c-5b8a1e4f7c20","signer_id":"01a15a31-0c2d-7e45-8b6f-2a9d4c7e1f03","sequence":3,"event_type":"signer.signed","payload":{"type":"typed","has_image":false},"hash_prev":"1d0e7c4b9a2f5e8d3c6b1a0f9e4d7c2b5a8f3e6d1c4b7a0e9f2d5c8b1a4e7f3d","hash_self":"8a3f6c9e2d5b1a4f7e0c3b6d9a2e5f8c1b4d7a0e3f6c9b2d5a8e1f4c7b0d3a6e","hash_version":2,"ip_address":"81.230.44.17","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15","occurred_at":"2026-10-13T06:52:44+00:00"},{"id":"01a15b0b-2a43-71c2-8e5f-3d6a9b2c5e81","signer_id":null,"sequence":4,"event_type":"signature.completed","payload":{"signed_count":1},"hash_prev":"8a3f6c9e2d5b1a4f7e0c3b6d9a2e5f8c1b4d7a0e3f6c9b2d5a8e1f4c7b0d3a6e","hash_self":"f2b5e8a1d4c7b0e3a6d9c2f5b8e1a4d7c0f3b6e9a2d5c8f1b4e7a0d3c6f9b2e5","hash_version":2,"ip_address":"81.230.44.17","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15","occurred_at":"2026-10-13T06:52:44+00:00"}],"chain":{"signature_request_id":"01a15a31-0c1f-7b90-a2e4-3d7c9b5f8e16","status":"ok","format":"current","event_count":4,"head":"f2b5e8a1d4c7b0e3a6d9c2f5b8e1a4d7c0f3b6e9a2d5c8f1b4e7a0d3c6f9b2e5","broken_at":null},"deal_room_id":null,"created_at":"2026-10-12T08:14:03+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/signatures/{signature}/cancel":{"parameters":[{"$ref":"#/components/parameters/SignatureRequestId"}],"post":{"operationId":"cancelSignatureRequest","tags":["Signature requests"],"summary":"Cancel a signature request","description":"Withdraws a request that is out for signing. Its status becomes `canceled`, the signers' links stop working, `signature.canceled` (with your `reason`) is added to the audit trail, the `signature.canceled` webhook fires and the document's `lifecycle_status` becomes `canceled`. Nobody is emailed.\n\nA request that is already `completed` or `canceled` is returned unchanged — a finished signature is never voided — so check `status` in the response. To send the document again, call `POST /documents/{document}/send`.","x-scope":"write","x-module":null,"x-permissions":["signatures.cancel"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":["string","null"],"maxLength":500,"description":"Why it was canceled; kept in the audit trail and in `cancel_reason`."}},"additionalProperties":false},"example":{"reason":"Fel pris i bilaga 2 — skickar en ny version."}}}},"responses":{"200":{"description":"The signature request, with its audit trail.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SignatureRequest"}},"additionalProperties":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/templates":{"get":{"operationId":"listTemplates","tags":["Templates"],"summary":"List templates","description":"Active templates — the organization's own and Closepact's built-in library — most used first, a page at a time. Each carries a `preview` (how many content rows it holds, per section); add `include_content=1` to get the full `content` as well, or fetch one with `GET /templates/{template}`.","x-scope":"read","x-module":null,"x-permissions":[],"parameters":[{"name":"filter[type]","in":"query","required":false,"description":"Only templates of this type. Repeat as `filter[type][]` for several.","schema":{"type":"string","enum":["room","document","proposal","map","email"]}},{"name":"q","in":"query","required":false,"description":"Case-insensitive match anywhere in the name.","schema":{"type":"string"},"example":"Serviceavtal"},{"name":"include_content","in":"query","required":false,"description":"Include each template's full `content`.","schema":{"type":"boolean","default":false}},{"name":"limit","in":"query","required":false,"description":"Items per page. Values outside 1–100 are clamped.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of templates.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Template"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a14f90-5d2c-7a18-9e3b-4c6d8f0a2b71","name":"Serviceavtal – fastighetsskötsel","type":"room","scope":"organization","is_built_in":false,"description":"Rum för nya serviceavtal: offert, avtal för signering och tidplan för uppstart.","is_active":true,"usage_count":37,"last_used_at":"2026-10-12T08:10:55+00:00","created_at":"2026-03-02T09:30:00+00:00","preview":{"sections":{"overview":1,"documents":2,"pricing":1},"content_count":4,"has_branding":true}}],"links":{"first":null,"last":null,"prev":null,"next":null},"meta":{"path":"https://api.closepact.com/v1/api/templates","per_page":25,"next_cursor":null,"prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createTemplate","tags":["Templates"],"summary":"Create a template","description":"Adds a template to the organization's library. `content` is stored as given and read by the part of Closepact the `type` belongs to; for a `room` template it holds `contents` — rows with a `section` (`overview`, `documents`, `pricing`, `map`, `attachments`), a `kind` (`text`, `video`, `link`, `document`, …), and `title`, `body` or `url` — and optionally `brand_overrides`. The easiest way to get a valid `content` is to copy it from an existing template (`GET /templates/{template}`).\n\n`scope` defaults to `organization`.","x-scope":"write","x-module":null,"x-permissions":["templates.create"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","type"],"properties":{"name":{"type":"string","minLength":2,"maxLength":255},"type":{"type":"string","enum":["room","document","proposal","map","email"]},"scope":{"type":"string","enum":["personal","team","organization"],"default":"organization"},"description":{"type":["string","null"],"maxLength":5000},"content":{"type":"object","description":"The template's body; its shape depends on `type`. Defaults to empty.","additionalProperties":true},"team_id":{"type":["string","null"],"format":"uuid","description":"The team a `team` template belongs to: one of this organization's teams. Any other id fails validation (`422`)."}},"additionalProperties":false},"example":{"name":"Serviceavtal – fastighetsskötsel","type":"room","description":"Rum för nya serviceavtal: offert, avtal för signering och tidplan för uppstart.","content":{"contents":[{"section":"overview","kind":"text","title":"Välkommen","body":"Här samlar vi allt inför uppstarten av ert serviceavtal."},{"section":"documents","kind":"link","title":"Våra villkor","url":"https://nordiskfastighet.se/villkor"}]}}}}},"responses":{"201":{"description":"The new template.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Template"}},"additionalProperties":false},"example":{"data":{"id":"01a15c44-1f8e-7b3a-a2d5-6e9c0b4f7a12","name":"Serviceavtal – fastighetsskötsel","type":"room","scope":"organization","is_built_in":false,"description":"Rum för nya serviceavtal: offert, avtal för signering och tidplan för uppstart.","is_active":true,"usage_count":null,"last_used_at":null,"created_at":"2026-10-18T09:02:11+00:00","preview":{"sections":{"overview":1,"documents":1},"content_count":2,"has_branding":false}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/templates/{template}":{"parameters":[{"name":"template","in":"path","required":true,"description":"The template's id.","schema":{"type":"string","format":"uuid"},"example":"01a14f90-5d2c-7a18-9e3b-4c6d8f0a2b71"}],"get":{"operationId":"getTemplate","tags":["Templates"],"summary":"Retrieve a template","description":"One template, always with its full `content`. Works for the organization's own templates and for Closepact's built-in library; another organization's template answers `404`.","x-scope":"read","x-module":null,"x-permissions":[],"responses":{"200":{"description":"The template.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Template"}},"additionalProperties":false},"example":{"data":{"id":"01a14f90-5d2c-7a18-9e3b-4c6d8f0a2b71","name":"Serviceavtal – fastighetsskötsel","type":"room","scope":"organization","is_built_in":false,"description":"Rum för nya serviceavtal: offert, avtal för signering och tidplan för uppstart.","is_active":true,"usage_count":37,"last_used_at":"2026-10-12T08:10:55+00:00","created_at":"2026-03-02T09:30:00+00:00","preview":{"sections":{"overview":1,"documents":1},"content_count":2,"has_branding":false},"content":{"contents":[{"section":"overview","kind":"text","title":"Välkommen","body":"Här samlar vi allt inför uppstarten av ert serviceavtal."},{"section":"documents","kind":"link","title":"Våra villkor","url":"https://nordiskfastighet.se/villkor"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/time-entries":{"get":{"operationId":"listTimeEntries","tags":["Time entries"],"summary":"List time entries","description":"Reported time, newest first, at most 500 entries per call and not paginated — narrow by date instead. For payroll, ask for a pay period's approved time: `filter[status]=approved&from=2026-11-01&to=2026-11-30T23:59:59Z`, and use `duration_minutes` (paid minutes, break deducted), `type` and `hourly_rate_cents`.\n\n`from` and `to` filter on `started_at`. An entry is `approved` only once somebody with `booking.time.approve` signed it off; only approved time reaches an invoice.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"filter[employee_id]","in":"query","required":false,"description":"Only this employee's time.","schema":{"type":"string","format":"uuid"}},{"name":"filter[status]","in":"query","required":false,"description":"Only these statuses. Repeat as `filter[status][]=submitted&filter[status][]=approved` for several.","schema":{"type":"string","enum":["open","submitted","approved","rejected"],"description":"`open`: clocked in, still running. `submitted`: clocked out, waiting for approval. `approved`: signed off — billable, and ready for payroll. `rejected`: sent back, with a `rejection_reason`."},"example":"approved"},{"name":"filter[work_order_id]","in":"query","required":false,"description":"Only time on this job.","schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","required":false,"description":"Entries that started at or after this instant. A date means midnight UTC. ISO 8601.","schema":{"type":"string","format":"date-time"},"example":"2026-11-01T00:00:00Z"},{"name":"to","in":"query","required":false,"description":"Entries that started at or before this instant. A bare date means midnight UTC at the start of that day. ISO 8601.","schema":{"type":"string","format":"date-time"},"example":"2026-11-30T23:59:59Z"}],"responses":{"200":{"description":"The entries, newest first (up to 500).","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TimeEntryListItem"}}},"additionalProperties":false},"example":{"data":[{"id":"01a15c20-4e5f-7253-a4b5-c6d71e243078","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","started_at":"2026-11-03T07:04:00.000000Z","clock_in_received_at":"2026-11-03T07:04:02.000000Z","ended_at":"2026-11-03T11:12:00.000000Z","clock_out_received_at":"2026-11-03T11:12:03.000000Z","break_minutes":15,"duration_minutes":233,"type":"work","start_lat":59.85861,"start_lng":17.63893,"start_accuracy_m":12,"start_source":"gps","end_lat":59.85858,"end_lng":17.63901,"end_accuracy_m":9,"end_source":"gps","within_geofence":true,"status":"approved","approved_by":"01a14eb3-c634-7238-bc76-18b09b287f98","approved_at":"2026-11-04T07:30:00.000000Z","rejection_reason":null,"billable":true,"hourly_rate_cents":32000,"billing_rate_cents":65000,"note":"Extra tid på ugnen.","metadata":[],"created_at":"2026-11-03T07:04:00.000000Z","updated_at":"2026-11-04T07:30:00.000000Z","employee":{"id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","first_name":"Ali","last_name":"Hassan"},"work_order":{"id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala"}}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/time-entries/approve":{"post":{"operationId":"approveTimeEntries","tags":["Time entries"],"summary":"Approve time entries","description":"Signs off a batch of entries — a week's timesheet at once. Only `submitted` entries are approved; any other id in the list (open, already approved, rejected, unknown or another organization's) is skipped, and `approved` tells you how many went through.\n\nApproving stamps `approved_by` (the member the key acts as) and `approved_at`, and copies each employee's current `hourly_cost_cents` onto the entry as `hourly_rate_cents` and `hourly_billing_rate_cents` as `billing_rate_cents`, so a later pay rise or price change does not rewrite what this time cost or is billed at. Approved time is billable and can no longer be edited or deleted without being rejected first.","x-scope":"write","x-module":"booking","x-permissions":["booking.time.approve"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids"],"properties":{"ids":{"type":"array","minItems":1,"description":"The entries to approve.","items":{"type":"string","format":"uuid"}}},"additionalProperties":false},"example":{"ids":["01a15c20-4e5f-7253-a4b5-c6d71e243078","01a15c20-4e60-7364-b5c6-d7e82f354189"]}}}},"responses":{"200":{"description":"How many entries were approved.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["approved"],"properties":{"approved":{"type":"integer","description":"Entries that went from `submitted` to `approved`."}},"additionalProperties":false}},"additionalProperties":false},"example":{"data":{"approved":2}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/time-entries/{timeEntry}/reject":{"post":{"operationId":"rejectTimeEntry","tags":["Time entries"],"summary":"Reject a time entry","description":"Sends an entry back with a reason, whatever its status — including an approved one, which is how approved time is corrected. The entry becomes `rejected` and editable again; `approved_by` records who rejected it and `approved_at` is cleared.","x-scope":"write","x-module":"booking","x-permissions":["booking.time.approve"],"parameters":[{"name":"timeEntry","in":"path","required":true,"description":"The time entry id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","description":"Why, for the person who reported it.","maxLength":500}},"additionalProperties":false},"example":{"reason":"Fel uppdrag — tiden hör till 1043."}}}},"responses":{"200":{"description":"The rejected entry.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/TimeEntry"}},"additionalProperties":false},"example":{"data":{"id":"01a15c20-4e5f-7253-a4b5-c6d71e243078","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","started_at":"2026-11-03T07:04:00.000000Z","clock_in_received_at":"2026-11-03T07:04:02.000000Z","ended_at":"2026-11-03T11:12:00.000000Z","clock_out_received_at":"2026-11-03T11:12:03.000000Z","break_minutes":15,"duration_minutes":233,"type":"work","start_lat":59.85861,"start_lng":17.63893,"start_accuracy_m":12,"start_source":"gps","end_lat":59.85858,"end_lng":17.63901,"end_accuracy_m":9,"end_source":"gps","within_geofence":true,"status":"rejected","approved_by":"01a14eb3-c634-7238-bc76-18b09b287f98","approved_at":null,"rejection_reason":"Fel uppdrag — tiden hör till 1043.","billable":true,"hourly_rate_cents":32000,"billing_rate_cents":65000,"note":"Extra tid på ugnen.","metadata":[],"created_at":"2026-11-03T07:04:00.000000Z","updated_at":"2026-11-04T08:02:00.000000Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/time-entries/{timeEntry}":{"patch":{"operationId":"updateTimeEntry","tags":["Time entries"],"summary":"Correct a time entry","description":"Corrects an entry that is not approved (`open`, `submitted` or `rejected`); an approved one answers 409 — reject it first, which leaves a trace. The status does not change.\n\n`duration_minutes` is recomputed from the result. A changed start or end is marked as typed in by the office (`start_source`/`end_source` become `office`), so a timesheet can tell corrections from clock readings.","x-scope":"write","x-module":"booking","x-permissions":["booking.time.approve"],"parameters":[{"name":"timeEntry","in":"path","required":true,"description":"The time entry id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"started_at":{"type":"string","format":"date-time","description":"New start. ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC."},"ended_at":{"type":["string","null"],"format":"date-time","description":"New end; after `started_at` when both are sent. ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC."},"break_minutes":{"type":"integer","description":"Unpaid break in minutes, 0–1440.","minimum":0,"maximum":1440},"work_order_id":{"type":["string","null"],"format":"uuid","description":"Move the time to another of your jobs, or null for none."},"billable":{"type":"boolean","description":"Whether the time may be invoiced."},"note":{"type":["string","null"],"description":"Free text."}},"additionalProperties":false},"example":{"ended_at":"2026-11-03T12:30:00+01:00","break_minutes":30}}}},"responses":{"200":{"description":"The corrected entry.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/TimeEntry"}},"additionalProperties":false},"example":{"data":{"id":"01a15c20-4e5f-7253-a4b5-c6d71e243078","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","started_at":"2026-11-03T07:04:00.000000Z","clock_in_received_at":"2026-11-03T07:04:02.000000Z","ended_at":"2026-11-03T11:30:00.000000Z","clock_out_received_at":"2026-11-03T11:30:03.000000Z","break_minutes":30,"duration_minutes":236,"type":"work","start_lat":59.85861,"start_lng":17.63893,"start_accuracy_m":12,"start_source":"gps","end_lat":59.85858,"end_lng":17.63901,"end_accuracy_m":9,"end_source":"office","within_geofence":true,"status":"submitted","approved_by":null,"approved_at":null,"rejection_reason":null,"billable":true,"hourly_rate_cents":null,"billing_rate_cents":null,"note":"Extra tid på ugnen.","metadata":[],"created_at":"2026-11-03T07:04:00.000000Z","updated_at":"2026-11-04T07:10:00.000000Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The entry is approved.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConflictError"},"example":{"message":"An approved entry has to be rejected before it can change.","error":"conflict"}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/webhooks":{"get":{"operationId":"listWebhookEndpoints","tags":["Webhooks"],"summary":"List webhook endpoints","description":"Every webhook endpoint in the organization, newest first, with the events each one receives and when it last succeeded or failed. Signing secrets are never included.","x-scope":"admin","x-module":null,"x-permissions":[],"responses":{"200":{"description":"The organization's webhook endpoints.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}}},"additionalProperties":false},"example":{"data":[{"id":"01a1c3e0-4b2f-7d19-9c55-2f6a8e1b7d30","url":"https://integration.nordiskfastighet.se/closepact/webhooks","description":"Affärer och signeringar till vårt affärssystem","events":["deal.won","deal.lost","signature.completed"],"status":"active","last_success_at":"2026-10-18T09:14:06+00:00","last_failure_at":null,"created_at":"2026-10-02T13:40:12+00:00","updated_at":"2026-10-02T13:40:12+00:00"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/WebhookForbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createWebhookEndpoint","tags":["Webhooks"],"summary":"Create a webhook endpoint","description":"Registers a URL to receive the events listed in `events`, or every event with `[\"*\"]`. The endpoint starts `active`.\n\nThe response contains the endpoint's signing secret (`secret`, starting `whsec_`). **It is shown this once and cannot be read again** — store it with your receiver now. If it is lost, rotate it with POST /webhooks/{endpoint}/rotate-secret.\n\nUnknown event names are refused with a 422 that lists them, so a typo cannot leave an endpoint waiting for an event that never comes. Send a test event with POST /webhooks/{endpoint}/test to check the receiver end to end.","x-scope":"admin","x-module":null,"x-permissions":["webhooks.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointCreate"},"example":{"url":"https://integration.nordiskfastighet.se/closepact/webhooks","events":["deal.won","deal.lost","signature.completed"],"description":"Affärer och signeringar till vårt affärssystem"}}}},"responses":{"201":{"description":"The endpoint, with its signing secret — shown only in this response.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}},"additionalProperties":false},"example":{"data":{"id":"01a1c3e0-4b2f-7d19-9c55-2f6a8e1b7d30","url":"https://integration.nordiskfastighet.se/closepact/webhooks","description":"Affärer och signeringar till vårt affärssystem","events":["deal.won","deal.lost","signature.completed"],"status":"active","last_success_at":null,"last_failure_at":null,"created_at":"2026-10-02T13:40:12+00:00","updated_at":"2026-10-02T13:40:12+00:00","secret":"whsec_Q3n8vT1kXz5bLm2RfY7wHc4pJd9sGa6E"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/WebhookForbidden"},"422":{"$ref":"#/components/responses/WebhookValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/webhooks/{endpoint}":{"parameters":[{"$ref":"#/components/parameters/WebhookEndpointId"}],"get":{"operationId":"getWebhookEndpoint","tags":["Webhooks"],"summary":"Retrieve a webhook endpoint","description":"One webhook endpoint: its URL, events, status and when a delivery last succeeded or failed. The signing secret is never included.","x-scope":"admin","x-module":null,"x-permissions":[],"responses":{"200":{"description":"The endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointEnvelope"},"example":{"data":{"id":"01a1c3e0-4b2f-7d19-9c55-2f6a8e1b7d30","url":"https://integration.nordiskfastighet.se/closepact/webhooks","description":"Affärer och signeringar till vårt affärssystem","events":["deal.won","deal.lost","signature.completed"],"status":"active","last_success_at":"2026-10-18T09:14:06+00:00","last_failure_at":"2026-10-11T22:03:51+00:00","created_at":"2026-10-02T13:40:12+00:00","updated_at":"2026-10-02T13:40:12+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/WebhookForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateWebhookEndpoint","tags":["Webhooks"],"summary":"Update a webhook endpoint","description":"Changes only the fields you send. Set `status` to `paused` to stop new events reaching the endpoint without deleting it (retries already scheduled still run), and back to `active` to resume; events that happened while it was paused are not sent afterwards. `events` replaces the whole list. `description: null` clears the description. The signing secret is unchanged — see POST /webhooks/{endpoint}/rotate-secret.","x-scope":"admin","x-module":null,"x-permissions":["webhooks.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointUpdate"},"example":{"events":["deal.won","deal.lost","signature.completed","invoice.issued"],"status":"active"}}}},"responses":{"200":{"description":"The endpoint as it now stands.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointEnvelope"},"example":{"data":{"id":"01a1c3e0-4b2f-7d19-9c55-2f6a8e1b7d30","url":"https://integration.nordiskfastighet.se/closepact/webhooks","description":"Affärer och signeringar till vårt affärssystem","events":["deal.won","deal.lost","signature.completed","invoice.issued"],"status":"active","last_success_at":"2026-10-18T09:14:06+00:00","last_failure_at":null,"created_at":"2026-10-02T13:40:12+00:00","updated_at":"2026-10-18T10:02:44+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/WebhookForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/WebhookValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteWebhookEndpoint","tags":["Webhooks"],"summary":"Delete a webhook endpoint","description":"Deletes the endpoint and its delivery history. Deliveries still waiting for a retry are dropped. To stop events for a while instead, set `status` to `paused`.","x-scope":"admin","x-module":null,"x-permissions":["webhooks.manage"],"responses":{"204":{"description":"Deleted. No body."},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/WebhookForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/webhooks/{endpoint}/deliveries":{"parameters":[{"$ref":"#/components/parameters/WebhookEndpointId"}],"get":{"operationId":"listWebhookDeliveries","tags":["Webhooks"],"summary":"List an endpoint's deliveries","description":"Every event queued for this endpoint, newest first, with its status, the number of attempts, your endpoint's last response (status and the first 2,000 characters of the body) or the connection error, and when the next retry is due. Filter on `filter[status]=abandoned` to find events your system never acknowledged.\n\nA delivery is `pending` until an attempt succeeds (`next_retry_at` says when the next one runs), `succeeded` once your endpoint answered with a 2xx, and `abandoned` after the seventh failed attempt. `failed` is only seen for the moment between a failed attempt and the scheduling of the next.","x-scope":"admin","x-module":null,"x-permissions":[],"parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/PerPage"},{"name":"filter[status]","in":"query","required":false,"description":"Only deliveries with this status.","schema":{"type":"string","enum":["pending","succeeded","failed","abandoned"]}},{"name":"filter[event]","in":"query","required":false,"description":"Only deliveries of this event, e.g. `invoice.issued`.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of deliveries.","content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a1c412-77d0-7a3e-b1c4-5e9f0a2d6c81","endpoint_id":"01a1c3e0-4b2f-7d19-9c55-2f6a8e1b7d30","event":"deal.won","event_id":"evt_3f9b2c1e-8a4d-4e6f-9b21-7c5d0e8a1f42","status":"succeeded","attempts":1,"response_status":200,"response_body":"{\"received\":true}","error":null,"payload":{"id":"01a1b9f2-2c6e-7f40-8d13-6b0e4a9c5f17","title":"Byggpartner Norden AB – serviceavtal 2027","value_cents":48000000,"currency":"SEK","stage":"contract_signed","status":"won","owner_id":"01a14eb3-c634-7238-bc76-18b09b287f98","account_id":"01a15a77-0d3b-7e21-a6f9-3c8e2b1d4a05"},"next_retry_at":null,"delivered_at":"2026-10-18T09:14:06+00:00","created_at":"2026-10-18T09:14:05+00:00","updated_at":"2026-10-18T09:14:06+00:00"},{"id":"01a1c3f8-0b91-7c02-8e6d-4a7f1c3b9e52","endpoint_id":"01a1c3e0-4b2f-7d19-9c55-2f6a8e1b7d30","event":"signature.completed","event_id":"evt_9a0d4e7b-2f61-4c3a-8b5e-1d7f9c2a6e04","status":"pending","attempts":2,"response_status":503,"response_body":"Service Unavailable","error":null,"payload":{"id":"01a1bc5d-93e4-7a10-b6c2-7e1f4d8a2c63","document_id":"01a1bc31-6f2a-7d84-9e15-3b8c0d4f7a29","deal_room_id":"01a1ba10-5e7c-7b88-9f02-1d4c6a8e3b29","title":"Serviceavtal 2027 – Byggpartner Norden AB","status":"completed","completed_at":"2026-10-18T08:51:17+00:00"},"next_retry_at":"2026-10-18T08:51:53+00:00","delivered_at":null,"created_at":"2026-10-18T08:51:17+00:00","updated_at":"2026-10-18T08:51:23+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":"https://api.closepact.com/v1/api/webhooks/01a1c3e0-4b2f-7d19-9c55-2f6a8e1b7d30/deliveries?per_page=2&cursor=eyJjcmVhdGVkX2F0IjoiMjAyNi0xMC0xOCAwODo1MToxNyJ9"},"meta":{"path":"https://api.closepact.com/v1/api/webhooks/01a1c3e0-4b2f-7d19-9c55-2f6a8e1b7d30/deliveries","per_page":2,"next_cursor":"eyJjcmVhdGVkX2F0IjoiMjAyNi0xMC0xOCAwODo1MToxNyJ9","prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/WebhookForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/webhooks/{endpoint}/test":{"parameters":[{"$ref":"#/components/parameters/WebhookEndpointId"}],"post":{"operationId":"sendWebhookTestEvent","tags":["Webhooks"],"summary":"Send a test event","description":"Queues a `ping` event for this endpoint alone — whatever events it subscribes to, and even while it is paused — signed with its secret and retried like any other event. Use it to check that your receiver is reachable and verifies signatures before real events depend on it.\n\nAnswers `202 Accepted` with the new delivery; it is normally sent within seconds. Follow its progress in GET /webhooks/{endpoint}/deliveries.","x-scope":"admin","x-module":null,"x-permissions":["webhooks.manage"],"responses":{"202":{"description":"The test delivery, queued.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WebhookDelivery"}},"additionalProperties":false},"example":{"data":{"id":"01a1c47b-2d58-7e93-a0f1-6c2b8d5e4a17","endpoint_id":"01a1c3e0-4b2f-7d19-9c55-2f6a8e1b7d30","event":"ping","event_id":"evt_c2e81f5a-7b3d-4f90-a6d2-5e8b1c4f7a39","status":"pending","attempts":0,"response_status":null,"response_body":null,"error":null,"payload":{"endpoint_id":"01a1c3e0-4b2f-7d19-9c55-2f6a8e1b7d30","message":"A test event from Closepact. Your endpoint received and can verify it."},"next_retry_at":null,"delivered_at":null,"created_at":"2026-10-18T09:20:31+00:00","updated_at":"2026-10-18T09:20:31+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/WebhookForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/webhooks/{endpoint}/rotate-secret":{"parameters":[{"$ref":"#/components/parameters/WebhookEndpointId"}],"post":{"operationId":"rotateWebhookSecret","tags":["Webhooks"],"summary":"Rotate the signing secret","description":"Replaces the endpoint's signing secret and returns the new one in `secret` — **shown this once**. The old secret stops working immediately: every request from now on, including retries of earlier events, is signed with the new one. To switch without rejecting anything, have your receiver accept a signature made with either the old or the new secret until the new one is deployed.","x-scope":"admin","x-module":null,"x-permissions":["webhooks.manage"],"responses":{"200":{"description":"The endpoint, with its new signing secret — shown only in this response.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}},"additionalProperties":false},"example":{"data":{"id":"01a1c3e0-4b2f-7d19-9c55-2f6a8e1b7d30","url":"https://integration.nordiskfastighet.se/closepact/webhooks","description":"Affärer och signeringar till vårt affärssystem","events":["deal.won","deal.lost","signature.completed"],"status":"active","last_success_at":"2026-10-18T09:14:06+00:00","last_failure_at":null,"created_at":"2026-10-02T13:40:12+00:00","updated_at":"2026-10-18T10:05:09+00:00","secret":"whsec_m4Rk8ZpW2tYc6NvB1xHq9LsD3fJg7Ue5"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/WebhookForbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/work-orders":{"get":{"operationId":"listWorkOrders","tags":["Work orders"],"summary":"List work orders","description":"Work orders (uppdrag) in schedule order: planned jobs by `planned_start`, then unplanned ones.\n\nTo read a calendar, pass a window with `from` and `to`. The window is an overlap test on the planned times, so a job that starts before `from` and is still running at `from` is included, and a two-day job appears on both weeks it touches. A window leaves out unplanned jobs (they have no start): ask for those with `filter[unplanned]=1`.\n\nEach work order includes its customer, service address and assignments; read one work order for its contact, services, checklist and materials. Cursor-paginated: follow `links.next`, or pass `meta.next_cursor` as `cursor`.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"q","in":"query","required":false,"description":"Search in the title and the order number (case-insensitive, substring).","schema":{"type":"string"},"example":"Storgatan"},{"name":"filter[status]","in":"query","required":false,"description":"Only these statuses. Repeat as `filter[status][]=booked&filter[status][]=scheduled` for several.","schema":{"type":"string","enum":["booked","scheduled","in_progress","completed","invoiced","cancelled"],"description":"`booked`: promised to the customer, nobody placed yet. `scheduled`: somebody is going. `in_progress`: they are there. `completed`: the work is done (and billable). `invoiced`: the money has been asked for. `cancelled`: not happening."},"example":"scheduled"},{"name":"filter[account_id]","in":"query","required":false,"description":"Only this customer's work orders.","schema":{"type":"string","format":"uuid"}},{"name":"filter[employee_id]","in":"query","required":false,"description":"Only work orders this employee is assigned to.","schema":{"type":"string","format":"uuid"}},{"name":"filter[unplanned]","in":"query","required":false,"description":"`1` for only jobs with no planned start.","schema":{"type":"boolean"}},{"name":"from","in":"query","required":false,"description":"Window start: jobs still planned to be running at or after this instant (or with no planned end). A date (`2026-11-02`) means midnight UTC. ISO 8601.","schema":{"type":"string","format":"date-time"},"example":"2026-11-02T00:00:00Z"},{"name":"to","in":"query","required":false,"description":"Window end: jobs planned to start at or before this instant. A bare date means midnight UTC at the start of that day, so to include all of Sunday send `2026-11-08T23:59:59Z`. ISO 8601.","schema":{"type":"string","format":"date-time"},"example":"2026-11-08T23:59:59Z"},{"name":"limit","in":"query","required":false,"description":"Items per page, 1–200.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of work orders.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","links","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WorkOrder"}},"links":{"$ref":"#/components/schemas/CursorLinks"},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"additionalProperties":false},"example":{"data":[{"id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala","description":"Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.","status":"scheduled","priority":1,"source":"manual","handover":null,"customer":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":"10231"},"service_address":{"id":"01a15c02-4e1c-7d5e-9f40-7182c9daeb23","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","account_id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","type":"service","label":"Lägenhet 1102","street":"Storgatan 12","street2":"Lgh 1102, 3 tr","zip":"753 20","city":"Uppsala","country":"SE","lat":59.8586,"lng":17.6389,"geofence_radius_m":150,"access_notes":"Portkod 4512. Nyckel i nyckelskåp vid tvättstugan.","contact_id":"01a15c02-4e1b-7c4d-8e3f-6071b8c9da12","is_default":true,"metadata":[],"created_at":"2026-09-02T09:15:00.000000Z","updated_at":"2026-09-02T09:15:00.000000Z","deleted_at":null},"pricing":{"model":"hourly","agreed_price_cents":null,"currency":"SEK","rot_rut":"rut","rot_rut_share":100},"schedule":{"planned_start":"2026-11-03T07:00:00+00:00","planned_end":"2026-11-03T11:00:00+00:00","earliest_start":"2026-11-03T07:00:00+00:00","latest_start":"2026-11-03T09:00:00+00:00","estimated_minutes":240,"actual_start":null,"actual_end":null,"actual_minutes":null},"assignments":[{"id":"01a15c11-0b1c-7f20-b162-93a4ebf10d45","role":"lead","status":"assigned","notified_at":"2026-10-28T13:02:11+00:00","accepted_at":null,"employee":{"id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","name":"Ali Hassan","color":"#2F80ED"}}],"completed_at":null,"cancelled_at":null,"cancel_reason":null,"customer_signed_at":null,"created_at":"2026-10-28T13:01:55+00:00","updated_at":"2026-10-28T13:02:11+00:00"}],"links":{"first":null,"last":null,"prev":null,"next":null},"meta":{"path":"https://api.closepact.com/v1/api/work-orders","per_page":50,"next_cursor":null,"prev_cursor":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createWorkOrder","tags":["Work orders"],"summary":"Create a work order","description":"Creates a job for one of your customers — from a webshop order, a CRM deal or a booking form. It starts as `booked` and takes the next number in the organization's work order series.\n\nGiving it `planned_start`/`planned_end` puts it on the calendar but assigns nobody; to send somebody, call `POST /schedule/assign`, which also moves the job to `scheduled`. The status cannot be set here: see `POST /work-orders/{workOrder}/status`.\n\n`account_id`, `contact_id`, `service_address_id` and `sub_entity_id` must belong to your organization; anything else is a 422. Amounts are öre excluding VAT.","x-scope":"write","x-module":"booking","x-permissions":["booking.work_orders.manage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["account_id","title"],"properties":{"account_id":{"type":"string","format":"uuid","description":"The customer (a customer id from the Customers API). Must be one of your organization's."},"title":{"type":"string","description":"What the job is.","maxLength":255},"contact_id":{"type":["string","null"],"format":"uuid","description":"A contact at the customer, or null."},"service_address_id":{"type":["string","null"],"format":"uuid","description":"Where the work is done: one of the customer's addresses, or null."},"description":{"type":["string","null"],"description":"Free text for the people doing the job."},"priority":{"type":"integer","description":"-2 to 2. Default 0.","minimum":-2,"maximum":2},"price_model":{"type":"string","enum":["hourly","fixed","per_unit"],"description":"How the customer is charged: by approved hours, a fixed `agreed_price_cents`, or per unit of the ordered services.","default":"hourly"},"agreed_price_cents":{"type":["integer","null"],"description":"Agreed price for a `fixed` job, in öre excluding VAT.","minimum":0},"rot_rut":{"type":"string","enum":["none","rot","rut"],"description":"Swedish tax deduction on the labour: ROT (repairs, 30 %) or RUT (household services, 50 %), computed on the invoice basis.","default":"none"},"rot_rut_share":{"type":["number","null"],"description":"Share of the deduction this customer claims, 0–100 percent.","minimum":0,"maximum":100},"planned_start":{"type":["string","null"],"format":"date-time","description":"ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC. Null for unplanned work."},"planned_end":{"type":["string","null"],"format":"date-time","description":"ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC. Must be after `planned_start`."},"earliest_start":{"type":["string","null"],"format":"date-time","description":"Customer's window, earliest acceptable start. ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC."},"latest_start":{"type":["string","null"],"format":"date-time","description":"Customer's window, latest acceptable start; not before `earliest_start`. ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC."},"estimated_minutes":{"type":["integer","null"],"description":"Expected duration in minutes.","minimum":1},"sub_entity_id":{"type":["string","null"],"format":"uuid","description":"The office (sub-entity) the job belongs to, for organizations with several. Must be one of yours."}},"additionalProperties":false},"example":{"account_id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","title":"Flyttstädning – Storgatan 12, Uppsala","contact_id":"01a15c02-4e1b-7c4d-8e3f-6071b8c9da12","service_address_id":"01a15c02-4e1c-7d5e-9f40-7182c9daeb23","description":"Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.","priority":1,"rot_rut":"rut","planned_start":"2026-11-03T08:00:00+01:00","planned_end":"2026-11-03T12:00:00+01:00","estimated_minutes":240}}}},"responses":{"201":{"description":"The work order, with its customer.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WorkOrder"}},"additionalProperties":false},"example":{"data":{"id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala","description":"Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.","status":"booked","priority":1,"source":"manual","handover":null,"customer":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":"10231"},"pricing":{"model":"hourly","agreed_price_cents":null,"currency":"SEK","rot_rut":"rut","rot_rut_share":100},"schedule":{"planned_start":"2026-11-03T07:00:00+00:00","planned_end":"2026-11-03T11:00:00+00:00","earliest_start":"2026-11-03T07:00:00+00:00","latest_start":"2026-11-03T09:00:00+00:00","estimated_minutes":240,"actual_start":null,"actual_end":null,"actual_minutes":null},"completed_at":null,"cancelled_at":null,"cancel_reason":null,"customer_signed_at":null,"created_at":"2026-10-28T13:01:55+00:00","updated_at":"2026-10-28T13:01:55+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/work-orders/{workOrder}":{"get":{"operationId":"getWorkOrder","tags":["Work orders"],"summary":"Get a work order","description":"One work order with everything on it: customer, contact, service address, assignments, ordered services, checklist and materials. A work order of another organization, or a deleted one, is a 404.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The work order.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WorkOrder"}},"additionalProperties":false},"example":{"data":{"id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala","description":"Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.","status":"scheduled","priority":1,"source":"manual","handover":null,"customer":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":"10231"},"contact":{"id":"01a15c02-4e1b-7c4d-8e3f-6071b8c9da12","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","account_id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","email":"karin.ek@brflinden.se","name":"Karin Ek","title":"Ordförande","phone":"070-412 33 08","crm_external_id":null,"crm_provider":null,"metadata":null,"created_at":"2026-09-02T09:14:00.000000Z","updated_at":"2026-09-02T09:14:00.000000Z","deleted_at":null},"service_address":{"id":"01a15c02-4e1c-7d5e-9f40-7182c9daeb23","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","account_id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","type":"service","label":"Lägenhet 1102","street":"Storgatan 12","street2":"Lgh 1102, 3 tr","zip":"753 20","city":"Uppsala","country":"SE","lat":59.8586,"lng":17.6389,"geofence_radius_m":150,"access_notes":"Portkod 4512. Nyckel i nyckelskåp vid tvättstugan.","contact_id":"01a15c02-4e1b-7c4d-8e3f-6071b8c9da12","is_default":true,"metadata":[],"created_at":"2026-09-02T09:15:00.000000Z","updated_at":"2026-09-02T09:15:00.000000Z","deleted_at":null},"pricing":{"model":"hourly","agreed_price_cents":null,"currency":"SEK","rot_rut":"rut","rot_rut_share":100},"schedule":{"planned_start":"2026-11-03T07:00:00+00:00","planned_end":"2026-11-03T11:00:00+00:00","earliest_start":"2026-11-03T07:00:00+00:00","latest_start":"2026-11-03T09:00:00+00:00","estimated_minutes":240,"actual_start":null,"actual_end":null,"actual_minutes":null},"assignments":[{"id":"01a15c11-0b1c-7f20-b162-93a4ebf10d45","role":"lead","status":"assigned","notified_at":"2026-10-28T13:02:11+00:00","accepted_at":null,"employee":{"id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","name":"Ali Hassan","color":"#2F80ED"}}],"services":[{"id":"01a15c14-4e5f-7253-a4b5-c6d71e243078","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","service_id":"01a15b80-5a6b-7c8d-9eaf-b0c1d2e3f405","name":"Flyttstädning","quantity":4,"unit_price_cents":49500,"discount_percent":0,"vat_rate":25,"rot_rut_eligible":true,"sort_order":0,"created_at":"2026-10-28T13:01:55.000000Z","updated_at":"2026-10-28T13:01:55.000000Z"}],"checklist":[{"id":"01a15c12-2c3d-7031-8273-a4b5fc021e56","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","label":"Rengör ugn, fläkt och kylskåp","is_done":false,"is_required":true,"done_by":null,"done_at":null,"sort_order":0,"created_at":"2026-10-28T13:05:40.000000Z","updated_at":"2026-10-28T13:05:40.000000Z"}],"materials":[],"completed_at":null,"cancelled_at":null,"cancel_reason":null,"customer_signed_at":null,"created_at":"2026-10-28T13:01:55+00:00","updated_at":"2026-10-28T13:02:11+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateWorkOrder","tags":["Work orders"],"summary":"Update a work order","description":"Changes the fields you send and leaves the rest. Sending `null` clears a nullable field.\n\nWhat this does not do: change the status (use `POST /work-orders/{workOrder}/status`), or check the new times against anybody's calendar — moving a job that has people on it is `POST /schedule/move`, which warns about double bookings. Changing `planned_start` here never changes the status either.","x-scope":"write","x-module":"booking","x-permissions":["booking.work_orders.manage"],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"account_id":{"type":"string","format":"uuid","description":"The customer (a customer id from the Customers API). Must be one of your organization's."},"title":{"type":"string","description":"What the job is.","maxLength":255},"contact_id":{"type":["string","null"],"format":"uuid","description":"A contact at the customer, or null."},"service_address_id":{"type":["string","null"],"format":"uuid","description":"Where the work is done: one of the customer's addresses, or null."},"description":{"type":["string","null"],"description":"Free text for the people doing the job."},"priority":{"type":"integer","description":"-2 to 2. Default 0.","minimum":-2,"maximum":2},"price_model":{"type":"string","enum":["hourly","fixed","per_unit"],"description":"How the customer is charged: by approved hours, a fixed `agreed_price_cents`, or per unit of the ordered services.","default":"hourly"},"agreed_price_cents":{"type":["integer","null"],"description":"Agreed price for a `fixed` job, in öre excluding VAT.","minimum":0},"rot_rut":{"type":"string","enum":["none","rot","rut"],"description":"Swedish tax deduction on the labour: ROT (repairs, 30 %) or RUT (household services, 50 %), computed on the invoice basis.","default":"none"},"rot_rut_share":{"type":["number","null"],"description":"Share of the deduction this customer claims, 0–100 percent.","minimum":0,"maximum":100},"planned_start":{"type":["string","null"],"format":"date-time","description":"ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC. Null for unplanned work."},"planned_end":{"type":["string","null"],"format":"date-time","description":"ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC. Must be after `planned_start`."},"earliest_start":{"type":["string","null"],"format":"date-time","description":"Customer's window, earliest acceptable start. ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC."},"latest_start":{"type":["string","null"],"format":"date-time","description":"Customer's window, latest acceptable start; not before `earliest_start`. ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC."},"estimated_minutes":{"type":["integer","null"],"description":"Expected duration in minutes.","minimum":1},"sub_entity_id":{"type":["string","null"],"format":"uuid","description":"The office (sub-entity) the job belongs to, for organizations with several. Must be one of yours."}},"additionalProperties":false},"example":{"description":"Portkod 4512. Nyckel i nyckelskåp vid tvättstugan.","priority":2,"agreed_price_cents":null}}}},"responses":{"200":{"description":"The work order, with its customer and assignments.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WorkOrder"}},"additionalProperties":false},"example":{"data":{"id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala","description":"Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.","status":"scheduled","priority":1,"source":"manual","handover":null,"customer":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":"10231"},"pricing":{"model":"hourly","agreed_price_cents":null,"currency":"SEK","rot_rut":"rut","rot_rut_share":100},"schedule":{"planned_start":"2026-11-03T07:00:00+00:00","planned_end":"2026-11-03T11:00:00+00:00","earliest_start":"2026-11-03T07:00:00+00:00","latest_start":"2026-11-03T09:00:00+00:00","estimated_minutes":240,"actual_start":null,"actual_end":null,"actual_minutes":null},"assignments":[{"id":"01a15c11-0b1c-7f20-b162-93a4ebf10d45","role":"lead","status":"assigned","notified_at":"2026-10-28T13:02:11+00:00","accepted_at":null,"employee":{"id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","name":"Ali Hassan","color":"#2F80ED"}}],"completed_at":null,"cancelled_at":null,"cancel_reason":null,"customer_signed_at":null,"created_at":"2026-10-28T13:01:55+00:00","updated_at":"2026-10-28T13:02:11+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteWorkOrder","tags":["Work orders"],"summary":"Delete a work order","description":"Deletes the job: it disappears from every list and read, and answers 404 from then on. Prefer cancelling (`POST /work-orders/{workOrder}/status` with `cancelled`) for a job the customer called off — that keeps the record and the reason. A deleted job that a recurrence wrote is not written again for that date.","x-scope":"write","x-module":"booking","x-permissions":["booking.work_orders.manage"],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The work order was deleted.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingDeleted"},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/work-orders/{workOrder}/status":{"post":{"operationId":"changeWorkOrderStatus","tags":["Work orders"],"summary":"Change the status of a work order","description":"Moves a job along its life. Only these transitions are allowed; anything else is a 409 and nothing changes:\n\n| From | May become |\n|---|---|\n| `booked` | `scheduled`, `cancelled` |\n| `scheduled` | `booked`, `in_progress`, `cancelled` |\n| `in_progress` | `completed`, `cancelled` |\n| `completed` | `invoiced` |\n| `invoiced`, `cancelled` | — (final) |\n\nSide effects: `in_progress` stamps `actual_start` with the current time. `cancelled` stamps `cancelled_at` and stores `reason` as `cancel_reason`. `completed` is the same as `POST /work-orders/{workOrder}/complete` with an empty body: `actual_end` defaults to now and `actual_start` to what was recorded, `completed_at` is now and `completed_by` the member the key acts as, it is refused (409) while a required checklist step is open or when the job is already completed, and it fires the `work_order.completed` webhook and makes the job billable. Use `/complete` instead when you have the checklist, materials, signature or corrected times to record with it.\n\nSet `invoiced` yourself when you bill a completed job outside Closepact (Fortnox, Visma, by hand); Closepact Invoicing sets it when it creates an invoice from the job. Assigning and unassigning move a job between `booked` and `scheduled` on their own.","x-scope":"write","x-module":"booking","x-permissions":["booking.work_orders.manage"],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["booked","scheduled","in_progress","completed","invoiced","cancelled"],"description":"`booked`: promised to the customer, nobody placed yet. `scheduled`: somebody is going. `in_progress`: they are there. `completed`: the work is done (and billable). `invoiced`: the money has been asked for. `cancelled`: not happening."},"reason":{"type":["string","null"],"description":"Why, when cancelling. Stored as `cancel_reason`.","maxLength":500}},"additionalProperties":false},"example":{"status":"cancelled","reason":"Kunden har sålt lägenheten och avbokat."}}}},"responses":{"200":{"description":"The work order in its new status.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WorkOrder"}},"additionalProperties":false},"example":{"data":{"id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala","description":"Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.","status":"cancelled","priority":1,"source":"manual","handover":null,"customer":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":"10231"},"pricing":{"model":"hourly","agreed_price_cents":null,"currency":"SEK","rot_rut":"rut","rot_rut_share":100},"schedule":{"planned_start":"2026-11-03T07:00:00+00:00","planned_end":"2026-11-03T11:00:00+00:00","earliest_start":"2026-11-03T07:00:00+00:00","latest_start":"2026-11-03T09:00:00+00:00","estimated_minutes":240,"actual_start":null,"actual_end":null,"actual_minutes":null},"assignments":[{"id":"01a15c11-0b1c-7f20-b162-93a4ebf10d45","role":"lead","status":"assigned","notified_at":"2026-10-28T13:02:11+00:00","accepted_at":null,"employee":{"id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","name":"Ali Hassan","color":"#2F80ED"}}],"completed_at":null,"cancelled_at":"2026-10-30T09:12:00+00:00","cancel_reason":"Kunden har sålt lägenheten och avbokat.","customer_signed_at":null,"created_at":"2026-10-28T13:01:55+00:00","updated_at":"2026-10-30T09:12:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The job may not move to that status from where it is, or a required checklist step is open.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConflictError"},"example":{"message":"A job that is scheduled cannot become completed.","error":"conflict"}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/work-orders/{workOrder}/complete":{"post":{"operationId":"completeWorkOrder","tags":["Work orders"],"summary":"Complete a work order","description":"Closes a job from the doorstep in one request: ticks checklist steps, adds the materials used, stores the customer's signature and note, corrects the times, and sets the status to `completed`. All of it is written or none of it is.\n\nOnly an `in_progress` job can be completed, and only once every required checklist step is done — ticks sent in this request count. A second call answers 409 instead of storing a second signature; if a retry after a timeout gets that 409, the first call went through.\n\nTimes: `actual_end` defaults to now and `actual_start` to what was recorded when the job started; send either to correct it. `completed_at` is now, and `completed_by` the member the key acts as.\n\nWhat completing triggers: the `work_order.completed` webhook; the job becomes billable, so its invoice basis (`GET /work-orders/{workOrder}/invoice-basis`) settles and Closepact Invoicing can invoice it, which moves it to `invoiced`.\n\nNeeds only `booking.read`: completing is reporting what was done, and the field worker who does it holds nothing more on booking.","x-scope":"write","x-module":"booking","x-permissions":["booking.read"],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"checklist":{"type":"array","description":"Ticks to record before completing. Every id must be a step on this job (422 otherwise).","items":{"type":"object","required":["id","is_done"],"properties":{"id":{"type":"string","format":"uuid","description":"A checklist item on this job."},"is_done":{"type":"boolean","description":"Ticked or not."}},"additionalProperties":false}},"materials":{"type":"array","description":"Material lines to add (added to any already on the job).","items":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"What was used.","maxLength":160},"quantity":{"type":"number","description":"How much, up to three decimals. Default 1.","minimum":0},"unit":{"type":["string","null"],"description":"Unit, e.g. `st`, `m`, `l`.","maxLength":32},"unit_price_cents":{"type":"integer","description":"Amount in öre (1 SEK = 100 öre), excluding VAT. Per unit. Default 0.","minimum":0},"vat_rate":{"type":"number","description":"VAT in percent. Default 25.","minimum":0,"maximum":100},"rot_rut_eligible":{"type":"boolean","description":"Whether the line counts toward ROT/RUT. Default false — the deduction is for labour."}},"additionalProperties":false}},"signature":{"type":["string","null"],"description":"The customer's signature: a PNG or JPEG image, base64-encoded, with or without a `data:image/png;base64,` prefix. Optional — the customer is not always home."},"signer_name":{"type":"string","description":"Name of the person who signed. Required with `signature`.","maxLength":160},"note":{"type":["string","null"],"description":"What the worker wants the office to know.","maxLength":2000},"actual_start":{"type":["string","null"],"format":"date-time","description":"Correct when the work started. ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC."},"actual_end":{"type":["string","null"],"format":"date-time","description":"When the work ended; default now. Not before the start (409). ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC."}},"additionalProperties":false},"example":{"checklist":[{"id":"01a15c12-2c3d-7031-8273-a4b5fc021e56","is_done":true}],"materials":[{"name":"Ugnsrengöring, sprayflaska","quantity":2,"unit":"st","unit_price_cents":8900}],"signature":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA…","signer_name":"Karin Ek","note":"Klart. Nycklarna lämnade i nyckelskåpet.","actual_end":"2026-11-03T12:12:00+01:00"}}}},"responses":{"200":{"description":"The completed work order, with its service address, checklist, materials and assignments.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WorkOrder"}},"additionalProperties":false},"example":{"data":{"id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala","description":"Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.","status":"completed","priority":1,"source":"manual","handover":null,"customer":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":"10231"},"service_address":{"id":"01a15c02-4e1c-7d5e-9f40-7182c9daeb23","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","account_id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","type":"service","label":"Lägenhet 1102","street":"Storgatan 12","street2":"Lgh 1102, 3 tr","zip":"753 20","city":"Uppsala","country":"SE","lat":59.8586,"lng":17.6389,"geofence_radius_m":150,"access_notes":"Portkod 4512. Nyckel i nyckelskåp vid tvättstugan.","contact_id":"01a15c02-4e1b-7c4d-8e3f-6071b8c9da12","is_default":true,"metadata":[],"created_at":"2026-09-02T09:15:00.000000Z","updated_at":"2026-09-02T09:15:00.000000Z","deleted_at":null},"pricing":{"model":"hourly","agreed_price_cents":null,"currency":"SEK","rot_rut":"rut","rot_rut_share":100},"schedule":{"planned_start":"2026-11-03T07:00:00+00:00","planned_end":"2026-11-03T11:00:00+00:00","earliest_start":"2026-11-03T07:00:00+00:00","latest_start":"2026-11-03T09:00:00+00:00","estimated_minutes":240,"actual_start":"2026-11-03T07:04:00+00:00","actual_end":"2026-11-03T11:12:00+00:00","actual_minutes":248},"assignments":[{"id":"01a15c11-0b1c-7f20-b162-93a4ebf10d45","role":"lead","status":"assigned","notified_at":"2026-10-28T13:02:11+00:00","accepted_at":null,"employee":{"id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","name":"Ali Hassan","color":"#2F80ED"}}],"checklist":[{"id":"01a15c12-2c3d-7031-8273-a4b5fc021e56","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","label":"Rengör ugn, fläkt och kylskåp","is_done":true,"is_required":true,"done_by":"01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091","done_at":"2026-11-03T10:41:27.000000Z","sort_order":0,"created_at":"2026-10-28T13:05:40.000000Z","updated_at":"2026-11-03T10:41:27.000000Z"}],"materials":[{"id":"01a15c13-3d4e-7142-9384-b5c60d132f67","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","name":"Ugnsrengöring, sprayflaska","quantity":2,"unit":"st","unit_price_cents":8900,"discount_percent":0,"vat_rate":25,"rot_rut_eligible":false,"added_by":"01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091","created_at":"2026-11-03T10:52:03.000000Z","updated_at":"2026-11-03T10:52:03.000000Z"}],"completed_at":"2026-11-03T11:12:40+00:00","cancelled_at":null,"cancel_reason":null,"customer_signed_at":"2026-11-03T11:12:40+00:00","created_at":"2026-10-28T13:01:55+00:00","updated_at":"2026-11-03T11:12:40+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Already completed, not `in_progress`, a required step is still open, or it would end before it started. `message` says which.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConflictError"},"example":{"message":"Required steps are not done: Rengör ugn, fläkt och kylskåp.","error":"conflict"}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/work-orders/{workOrder}/invoice-basis":{"get":{"operationId":"getWorkOrderInvoiceBasis","tags":["Work orders"],"summary":"Get the invoice basis of a work order","description":"What the job comes to, stated so that it can be invoiced in Closepact or elsewhere (Fortnox, Visma).\n\nThe price model decides what is billed: an `hourly` job bills the approved hours (at the single hourly price of the ordered services, or else each person's recorded rate); a `fixed` job bills `agreed_price_cents` and lists the hours as unbilled (with no agreed price it bills the hours and warns `fixed_price_missing`); a `per_unit` job bills the ordered services per unit and lists the hours as unbilled. Approved hours carry the employee's cost rate unless the ordered services name one price — a rate of 0 is billed as 0 and warned as `labour_rate_missing`. Materials are always their own lines. Only approved time counts; time still waiting for approval is reported under `time` and in `warnings`.\n\nAvailable in any status. Until the job is `completed` it will still change (warning `not_completed`); an `invoiced` job shows what was billed (`already_invoiced`).","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The invoice basis.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WorkOrderInvoiceBasis"}},"additionalProperties":false},"example":{"data":{"work_order":{"id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala","status":"completed","price_model":"hourly","currency":"SEK","agreed_price_cents":null,"completed_at":"2026-11-03T11:12:40+00:00","planned_start":"2026-11-03T07:00:00+00:00","account":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":"10231"}},"already_invoiced":false,"lines":[{"kind":"labour","description":"Ali Hassan","detail":"work","quantity":3.883,"unit_price_cents":49500,"discount_percent":0,"net_cents":192225,"vat_rate":25,"vat_cents":48056,"rot_rut_eligible":true,"billed":true,"excluded_reason":null,"source":{"type":"time_entries","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","minutes":233,"entries":1,"rate_basis":"employee_rate","cost_cents":124267}},{"kind":"material","description":"Ugnsrengöring, sprayflaska","detail":"st","quantity":2,"unit_price_cents":8900,"discount_percent":0,"net_cents":17800,"vat_rate":25,"vat_cents":4450,"rot_rut_eligible":false,"billed":true,"excluded_reason":null,"source":{"type":"material","work_order_material_id":"01a15c13-3d4e-7142-9384-b5c60d132f67"}}],"totals":{"labour_net_cents":192225,"labour_vat_cents":48056,"materials_net_cents":17800,"materials_vat_cents":4450,"net_cents":210025,"vat_cents":52506,"gross_cents":262531,"labour_cost_cents":124267},"rot_rut":{"type":"rut","rate_percent":50,"share_percent":100,"eligible_labour_net_cents":192225,"eligible_labour_gross_cents":240281,"claim_base_cents":240281,"deduction_cents":120141,"customer_pays_cents":142390,"annual_ceiling_applied":false},"time":{"approved_minutes":233,"awaiting_approval_minutes":0,"awaiting_approval_entries":0},"warnings":[]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/work-orders/{workOrder}/checklist":{"get":{"operationId":"listChecklistItems","tags":["Work orders"],"summary":"List the checklist of a work order","description":"The steps on a job, ordered by id (creation order). Show them by `sort_order`, which is what the field worker sees. Cursor-paginated: pass `next_cursor` as `cursor`.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1–200.","schema":{"type":"integer","minimum":1,"maximum":200,"default":100}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of checklist items.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BookingCursorPage"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChecklistItem"}}}}]},"example":{"data":[{"id":"01a15c12-2c3d-7031-8273-a4b5fc021e56","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","label":"Rengör ugn, fläkt och kylskåp","is_done":false,"is_required":true,"done_by":null,"done_at":null,"sort_order":0,"created_at":"2026-10-28T13:05:40.000000Z","updated_at":"2026-10-28T13:05:40.000000Z"}],"path":"https://api.closepact.com/v1/api/work-orders/01a15c10-8a2b-7e6f-a051-8293dae0fc34/checklist","per_page":100,"next_cursor":null,"next_page_url":null,"prev_cursor":null,"prev_page_url":null}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createChecklistItem","tags":["Work orders"],"summary":"Add a checklist item","description":"Adds a step to a job — at the end of the list unless you give `sort_order`. A required step (`is_required: true`) must be ticked before the job can be completed. The checklist of a `completed` or `invoiced` job cannot change (409).","x-scope":"write","x-module":"booking","x-permissions":["booking.work_orders.manage"],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["label"],"properties":{"label":{"type":"string","description":"What to do.","maxLength":255},"is_required":{"type":"boolean","description":"Whether the job can be completed without it. Default false."},"sort_order":{"type":"integer","description":"Position, from 0. Default: after the last item.","minimum":0}},"additionalProperties":false},"example":{"label":"Rengör ugn, fläkt och kylskåp","is_required":true}}}},"responses":{"201":{"description":"The new checklist item.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ChecklistItem"}},"additionalProperties":false},"example":{"data":{"id":"01a15c12-2c3d-7031-8273-a4b5fc021e56","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","label":"Rengör ugn, fläkt och kylskåp","is_done":false,"is_required":true,"done_by":null,"done_at":null,"sort_order":0,"created_at":"2026-10-28T13:05:40.000000Z","updated_at":"2026-10-28T13:05:40.000000Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The job is completed or invoiced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConflictError"},"example":{"message":"The checklist of a job that is completed cannot change.","error":"conflict"}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/work-orders/{workOrder}/checklist/{item}":{"patch":{"operationId":"updateChecklistItem","tags":["Work orders"],"summary":"Tick or change a checklist item","description":"Ticks or unticks a step, renames it, makes it required or moves it. Ticking stamps `done_by` (the member the key acts as) and `done_at`; unticking clears them; re-sending the same `is_done` leaves them alone. `done_by` and `done_at` are never taken from the request.\n\nNeeds only `booking.read`, so a field worker's key can tick. The checklist of a `completed` or `invoiced` job cannot change (409). An item of another job is a 404.","x-scope":"write","x-module":"booking","x-permissions":["booking.read"],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}},{"name":"item","in":"path","required":true,"description":"The checklist item id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","description":"What to do.","maxLength":255},"is_required":{"type":"boolean","description":"Whether the job can be completed without it. Default false."},"sort_order":{"type":"integer","description":"Position, from 0. Default: after the last item.","minimum":0},"is_done":{"type":"boolean","description":"Ticked or not."}},"additionalProperties":false},"example":{"is_done":true}}}},"responses":{"200":{"description":"The checklist item.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ChecklistItem"}},"additionalProperties":false},"example":{"data":{"id":"01a15c12-2c3d-7031-8273-a4b5fc021e56","organization_id":"01a14eb3-c5f0-7c55-8c2e-6a0e1f7b9d21","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","label":"Rengör ugn, fläkt och kylskåp","is_done":true,"is_required":true,"done_by":"01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091","done_at":"2026-11-03T10:41:27.000000Z","sort_order":0,"created_at":"2026-10-28T13:05:40.000000Z","updated_at":"2026-11-03T10:41:27.000000Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The job is completed or invoiced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConflictError"},"example":{"message":"The checklist of a job that is invoiced cannot change.","error":"conflict"}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteChecklistItem","tags":["Work orders"],"summary":"Remove a checklist item","description":"Removes a step. Not possible once the job is `completed` or `invoiced` (409).","x-scope":"write","x-module":"booking","x-permissions":["booking.work_orders.manage"],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}},{"name":"item","in":"path","required":true,"description":"The checklist item id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The checklist item was removed.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingDeleted"},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The job is completed or invoiced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConflictError"},"example":{"message":"The checklist of a job that is completed cannot change.","error":"conflict"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/work-orders/{workOrder}/materials":{"get":{"operationId":"listWorkOrderMaterials","tags":["Work orders"],"summary":"List the materials of a work order","description":"Material lines on a job, oldest first, each with its `net_cents`. Cursor-paginated: pass `next_cursor` as `cursor`.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1–200.","schema":{"type":"integer","minimum":1,"maximum":200,"default":100}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of material lines.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BookingCursorPage"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WorkOrderMaterial"}}}}]},"example":{"data":[{"id":"01a15c13-3d4e-7142-9384-b5c60d132f67","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","name":"Ugnsrengöring, sprayflaska","quantity":2,"unit":"st","unit_price_cents":8900,"discount_percent":0,"vat_rate":25,"rot_rut_eligible":false,"net_cents":17800,"added_by":"01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091","created_at":"2026-11-03T10:52:03+00:00","updated_at":"2026-11-03T10:52:03+00:00"}],"path":"https://api.closepact.com/v1/api/work-orders/01a15c10-8a2b-7e6f-a051-8293dae0fc34/materials","per_page":100,"next_cursor":null,"next_page_url":null,"prev_cursor":null,"prev_page_url":null}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createWorkOrderMaterial","tags":["Work orders"],"summary":"Add a material line","description":"Records something used on the job. It appears on the invoice basis as its own line. `added_by` is the member the key acts as.\n\nAllowed until the job is `invoiced` (409 after that) — including after completion, when the office adds what the van used. Needs only `booking.read`, so a field worker's key can report materials.","x-scope":"write","x-module":"booking","x-permissions":["booking.read"],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"What was used.","maxLength":160},"quantity":{"type":"number","description":"How much, up to three decimals. Default 1.","minimum":0},"unit":{"type":["string","null"],"description":"Unit, e.g. `st`, `m`, `l`.","maxLength":32},"unit_price_cents":{"type":"integer","description":"Amount in öre (1 SEK = 100 öre), excluding VAT. Per unit. Default 0.","minimum":0},"vat_rate":{"type":"number","description":"VAT in percent. Default 25.","minimum":0,"maximum":100},"rot_rut_eligible":{"type":"boolean","description":"Whether the line counts toward ROT/RUT. Default false — the deduction is for labour."}},"additionalProperties":false},"example":{"name":"Ugnsrengöring, sprayflaska","quantity":2,"unit":"st","unit_price_cents":8900,"vat_rate":25}}}},"responses":{"201":{"description":"The new material line.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WorkOrderMaterial"}},"additionalProperties":false},"example":{"data":{"id":"01a15c13-3d4e-7142-9384-b5c60d132f67","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","name":"Ugnsrengöring, sprayflaska","quantity":2,"unit":"st","unit_price_cents":8900,"discount_percent":0,"vat_rate":25,"rot_rut_eligible":false,"net_cents":17800,"added_by":"01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091","created_at":"2026-11-03T10:52:03+00:00","updated_at":"2026-11-03T10:52:03+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The job is invoiced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConflictError"},"example":{"message":"The materials of a job that is invoiced cannot change.","error":"conflict"}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/work-orders/{workOrder}/materials/{material}":{"patch":{"operationId":"updateWorkOrderMaterial","tags":["Work orders"],"summary":"Change a material line","description":"Changes the fields you send. Not possible once the job is `invoiced` (409). A line of another job is a 404.","x-scope":"write","x-module":"booking","x-permissions":["booking.read"],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}},{"name":"material","in":"path","required":true,"description":"The material line id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"What was used.","maxLength":160},"quantity":{"type":"number","description":"How much, up to three decimals. Default 1.","minimum":0},"unit":{"type":["string","null"],"description":"Unit, e.g. `st`, `m`, `l`.","maxLength":32},"unit_price_cents":{"type":"integer","description":"Amount in öre (1 SEK = 100 öre), excluding VAT. Per unit. Default 0.","minimum":0},"vat_rate":{"type":"number","description":"VAT in percent. Default 25.","minimum":0,"maximum":100},"rot_rut_eligible":{"type":"boolean","description":"Whether the line counts toward ROT/RUT. Default false — the deduction is for labour."}},"additionalProperties":false},"example":{"quantity":3}}}},"responses":{"200":{"description":"The material line.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WorkOrderMaterial"}},"additionalProperties":false},"example":{"data":{"id":"01a15c13-3d4e-7142-9384-b5c60d132f67","work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","name":"Ugnsrengöring, sprayflaska","quantity":3,"unit":"st","unit_price_cents":8900,"discount_percent":0,"vat_rate":25,"rot_rut_eligible":false,"net_cents":26700,"added_by":"01a14eb3-c6a1-7e2f-8a3b-4c5d6e7f8091","created_at":"2026-11-03T10:52:03+00:00","updated_at":"2026-11-03T13:20:00+00:00"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The job is invoiced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConflictError"},"example":{"message":"The materials of a job that is invoiced cannot change.","error":"conflict"}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteWorkOrderMaterial","tags":["Work orders"],"summary":"Remove a material line","description":"Removes a line. Needs `booking.work_orders.manage` (adding is reporting; removing a cost line is a decision). Not possible once the job is `invoiced` (409).","x-scope":"write","x-module":"booking","x-permissions":["booking.work_orders.manage"],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}},{"name":"material","in":"path","required":true,"description":"The material line id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The material line was removed.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingDeleted"},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The job is invoiced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConflictError"},"example":{"message":"The materials of a job that is invoiced cannot change.","error":"conflict"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/schedule/assign":{"post":{"operationId":"assignWorkOrder","tags":["Schedule"],"summary":"Assign an employee to a work order","description":"Sends an employee to a job, and optionally places the job in time in the same request. This is how an integration schedules work.\n\nBefore writing, the employee is checked at the job's (new) times against their other jobs, absences, availability rules and employment dates:\n\n- **Errors** — a double booking (another non-cancelled job of theirs overlaps; back-to-back is fine) or a date outside their employment — refuse the request with 409 `schedule_conflict` and the findings. Nothing is written. Send the same request with `force: true` to book anyway.\n- **Warnings** — outside their availability rules, or during an absence — never block. The assignment is written and the warnings come back in `conflicts`.\n\nA job with no times is not checked: assigning somebody to unplanned work reserves them before the day is agreed.\n\nEffects: a `booked` job that has a start moves to `scheduled`. Assigning the same employee again updates their assignment (role) instead of adding a second. A new assignment notifies the employee by email — and by SMS if the organization has turned that on — and sets `notified_at`. A work order or employee that is not your organization's is a 404.","x-scope":"write","x-module":"booking","x-permissions":["booking.schedule"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["work_order_id","employee_id"],"properties":{"work_order_id":{"type":"string","format":"uuid","description":"The work order."},"employee_id":{"type":"string","format":"uuid","description":"The employee to send."},"role":{"type":"string","enum":["lead","helper"],"description":"Default `lead`."},"planned_start":{"type":["string","null"],"format":"date-time","description":"Place the job at this time as well (otherwise its current times are used). ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC."},"planned_end":{"type":["string","null"],"format":"date-time","description":"End of that time; after `planned_start`. ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC."},"force":{"type":"boolean","description":"Write even if the check finds errors. Default false."}},"additionalProperties":false},"example":{"work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","role":"lead","planned_start":"2026-11-03T08:00:00+01:00","planned_end":"2026-11-03T12:00:00+01:00"}}}},"responses":{"201":{"description":"Assigned. `conflicts` lists the warnings (and, when forced, the errors) the slot has.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","conflicts","assignment_id"],"properties":{"data":{"$ref":"#/components/schemas/WorkOrder"},"conflicts":{"type":"array","description":"Warnings (and, with `force`, errors) that were overridden. Empty when the slot is clean.","items":{"$ref":"#/components/schemas/ScheduleFinding"}},"assignment_id":{"type":"string","format":"uuid","description":"The assignment, for unassigning later."}},"additionalProperties":false},"example":{"data":{"id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala","description":"Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.","status":"scheduled","priority":1,"source":"manual","handover":null,"customer":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":null},"pricing":{"model":"hourly","agreed_price_cents":null,"currency":"SEK","rot_rut":"rut","rot_rut_share":100},"schedule":{"planned_start":"2026-11-03T07:00:00+00:00","planned_end":"2026-11-03T11:00:00+00:00","earliest_start":"2026-11-03T07:00:00+00:00","latest_start":"2026-11-03T09:00:00+00:00","estimated_minutes":240,"actual_start":null,"actual_end":null,"actual_minutes":null},"assignments":[{"id":"01a15c11-0b1c-7f20-b162-93a4ebf10d45","role":"lead","status":"assigned","notified_at":"2026-10-28T13:02:11+00:00","accepted_at":null,"employee":{"id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","name":"Ali Hassan","color":"#2F80ED"}}],"completed_at":null,"cancelled_at":null,"cancel_reason":null,"customer_signed_at":null,"created_at":"2026-10-28T13:01:55+00:00","updated_at":"2026-10-28T13:02:11+00:00"},"conflicts":[{"code":"outside_working_hours","severity":"warning","message":"Ali Hassan does not normally work those hours.","context":{"hours":[{"start":"07:00:00","end":"11:30:00"},{"start":"12:15:00","end":"16:00:00"}]}}],"assignment_id":"01a15c11-0b1c-7f20-b162-93a4ebf10d45"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The assignment would clash (a double booking, or a date outside the employee's employment) and `force` was not set. Nothing was written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleConflictError"},"example":{"message":"That would clash.","error":"schedule_conflict","conflicts":[{"code":"double_booked","severity":"error","message":"Ali Hassan is already on 1042 then.","context":{"work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala","planned_start":"2026-11-03T07:00:00+00:00","planned_end":"2026-11-03T11:00:00+00:00"}}]}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/schedule/move":{"post":{"operationId":"moveWorkOrder","tags":["Schedule"],"summary":"Move a work order to a new time","description":"Moves a job to new planned times, keeping everybody on it. Each assigned employee is checked at the new times exactly as when assigning: errors refuse the move with 409 `schedule_conflict` unless `force` is true, warnings come back in `conflicts`. The status does not change. A work order that is not your organization's is a 404.","x-scope":"write","x-module":"booking","x-permissions":["booking.schedule"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["work_order_id","planned_start","planned_end"],"properties":{"work_order_id":{"type":"string","format":"uuid","description":"The work order."},"planned_start":{"type":"string","format":"date-time","description":"New start. ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC."},"planned_end":{"type":"string","format":"date-time","description":"New end; after `planned_start`. ISO 8601 date-time. An offset is honoured (`2026-11-03T08:00:00+01:00` is stored as 07:00 UTC); a value without one is read as UTC. Responses are always UTC."},"force":{"type":"boolean","description":"Write even if the check finds errors. Default false."}},"additionalProperties":false},"example":{"work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","planned_start":"2026-11-04T08:00:00+01:00","planned_end":"2026-11-04T12:00:00+01:00"}}}},"responses":{"200":{"description":"Moved.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data","conflicts"],"properties":{"data":{"$ref":"#/components/schemas/WorkOrder"},"conflicts":{"type":"array","description":"Findings for the people on the job at the new time.","items":{"$ref":"#/components/schemas/ScheduleFinding"}}},"additionalProperties":false},"example":{"data":{"id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala","description":"Tre rum och kök, 78 m². Kunden lämnar nycklarna kl. 08.00.","status":"scheduled","priority":1,"source":"manual","handover":null,"customer":{"id":"01a15c02-4e1a-7b3c-9d2e-5f60a7b8c901","name":"Brf Linden","customer_number":null},"pricing":{"model":"hourly","agreed_price_cents":null,"currency":"SEK","rot_rut":"rut","rot_rut_share":100},"schedule":{"planned_start":"2026-11-04T07:00:00+00:00","planned_end":"2026-11-04T11:00:00+00:00","earliest_start":"2026-11-03T07:00:00+00:00","latest_start":"2026-11-03T09:00:00+00:00","estimated_minutes":240,"actual_start":null,"actual_end":null,"actual_minutes":null},"assignments":[{"id":"01a15c11-0b1c-7f20-b162-93a4ebf10d45","role":"lead","status":"assigned","notified_at":"2026-10-28T13:02:11+00:00","accepted_at":null,"employee":{"id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01","name":"Ali Hassan","color":"#2F80ED"}}],"completed_at":null,"cancelled_at":null,"cancel_reason":null,"customer_signed_at":null,"created_at":"2026-10-28T13:01:55+00:00","updated_at":"2026-10-28T13:02:11+00:00"},"conflicts":[]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Somebody on the job would be double-booked or outside their employment, and `force` was not set. Nothing was written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleConflictError"},"example":{"message":"That would clash.","error":"schedule_conflict","conflicts":[{"code":"double_booked","severity":"error","message":"Ali Hassan is already on 1042 then.","context":{"work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala","planned_start":"2026-11-03T07:00:00+00:00","planned_end":"2026-11-03T11:00:00+00:00"}}]}}}},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/schedule/conflicts":{"get":{"operationId":"listScheduleConflicts","tags":["Schedule"],"summary":"List conflicts on the schedule","description":"Every problem on the board in a window: each planned job that overlaps the window and is not cancelled, completed or invoiced is checked for each employee on it, exactly as when assigning. Read-only — use it for a morning check or to show a warning banner. A double booking between two jobs is reported once from each side.","x-scope":"read","x-module":"booking","x-permissions":[],"parameters":[{"name":"from","in":"query","required":true,"description":"Window start. ISO 8601 date or date-time; a date means midnight UTC.","schema":{"type":"string","format":"date-time"},"example":"2026-11-02T00:00:00Z"},{"name":"to","in":"query","required":true,"description":"Window end, after `from`. ISO 8601 date or date-time.","schema":{"type":"string","format":"date-time"},"example":"2026-11-09T00:00:00Z"}],"responses":{"200":{"description":"The findings, possibly none.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleBoardFinding"}}},"additionalProperties":false},"example":{"data":[{"code":"double_booked","severity":"error","message":"Ali Hassan is already on 1042 then.","context":{"work_order_id":"01a15c10-8a2c-7f70-b162-93a4ebf10d45","order_number":"1043","title":"Fönsterputs – Kungsgatan 4, Uppsala","planned_start":"2026-11-03T08:00:00+00:00","planned_end":"2026-11-03T09:00:00+00:00"},"work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01"},{"code":"double_booked","severity":"error","message":"Ali Hassan is already on 1042 then.","context":{"work_order_id":"01a15c10-8a2b-7e6f-a051-8293dae0fc34","order_number":"1042","title":"Flyttstädning – Storgatan 12, Uppsala","planned_start":"2026-11-03T07:00:00+00:00","planned_end":"2026-11-03T11:00:00+00:00"},"work_order_id":"01a15c10-8a2c-7f70-b162-93a4ebf10d45","order_number":"1043","employee_id":"01a15b90-1f2e-7a3b-8c4d-5e6f7a8b9c01"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/work-orders/{workOrder}/assignments/{assignment}":{"delete":{"operationId":"unassignWorkOrder","tags":["Schedule"],"summary":"Take an employee off a work order","description":"Removes one assignment. A `scheduled` job left with nobody on it falls back to `booked`, so it shows up again among the work that needs placing. The employee is not notified. An assignment of another job is a 404.","x-scope":"write","x-module":"booking","x-permissions":["booking.schedule"],"parameters":[{"name":"workOrder","in":"path","required":true,"description":"The work order id.","schema":{"type":"string","format":"uuid"}},{"name":"assignment","in":"path","required":true,"description":"The assignment id, as `assignments[].id` on the work order or `assignment_id` from `POST /schedule/assign`.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The employee was taken off the job.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingDeleted"},"example":{"deleted":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"webhooks":{"deal.created":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Deals","x-module":"proposals","summary":"Deal created","description":"A deal was created — in the panel, through the API or by a CRM sync.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["deal.created"]},"data":{"$ref":"#/components/schemas/WebhookDealPayload"}}}]},"example":{"event":"deal.created","event_id":"evt_3f9b2c1e-8a4d-4e6f-9b21-7c5d0e8a1f42","created":1792314845,"data":{"id":"01a1b9f2-2c6e-7f40-8d13-6b0e4a9c5f17","title":"Byggpartner Norden AB – serviceavtal 2027","value_cents":48000000,"currency":"SEK","stage":"draft","status":"open","owner_id":"01a14eb3-c634-7238-bc76-18b09b287f98","account_id":"01a15a77-0d3b-7e21-a6f9-3c8e2b1d4a05"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"deal.stage_changed":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Deals","x-module":"proposals","summary":"Deal moved to another stage","description":"A deal moved from one pipeline stage to another, by hand or automatically (for example to `opened` when the buyer first opens the deal room). `from_stage` and `to_stage` say where it was and where it is now; `stage` equals `to_stage`.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["deal.stage_changed"]},"data":{"$ref":"#/components/schemas/WebhookDealStageChangedPayload"}}}]},"example":{"event":"deal.stage_changed","event_id":"evt_5c2a8f14-0e7b-4d39-a1f6-8b3e5d9c0a27","created":1792314845,"data":{"id":"01a1b9f2-2c6e-7f40-8d13-6b0e4a9c5f17","title":"Byggpartner Norden AB – serviceavtal 2027","value_cents":48000000,"currency":"SEK","stage":"in_negotiation","status":"open","owner_id":"01a14eb3-c634-7238-bc76-18b09b287f98","account_id":"01a15a77-0d3b-7e21-a6f9-3c8e2b1d4a05","from_stage":"engaged","to_stage":"in_negotiation","reason":"Kunden vill förhandla om uppsägningstiden"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"deal.won":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Deals","x-module":"proposals","summary":"Deal won","description":"A deal was marked won — by hand, or automatically when its contract was signed. Sent once per deal.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["deal.won"]},"data":{"$ref":"#/components/schemas/WebhookDealPayload"}}}]},"example":{"event":"deal.won","event_id":"evt_3f9b2c1e-8a4d-4e6f-9b21-7c5d0e8a1f42","created":1792314845,"data":{"id":"01a1b9f2-2c6e-7f40-8d13-6b0e4a9c5f17","title":"Byggpartner Norden AB – serviceavtal 2027","value_cents":48000000,"currency":"SEK","stage":"contract_signed","status":"won","owner_id":"01a14eb3-c634-7238-bc76-18b09b287f98","account_id":"01a15a77-0d3b-7e21-a6f9-3c8e2b1d4a05"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"deal.lost":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Deals","x-module":"proposals","summary":"Deal lost","description":"A deal was marked lost. `reason` is what the seller wrote, or null.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["deal.lost"]},"data":{"$ref":"#/components/schemas/WebhookDealLostPayload"}}}]},"example":{"event":"deal.lost","event_id":"evt_7d4e1a92-6c3b-4f58-9e07-2a5c8b1d3f60","created":1792314845,"data":{"id":"01a1b9f2-2c6e-7f40-8d13-6b0e4a9c5f17","title":"Byggpartner Norden AB – serviceavtal 2027","value_cents":48000000,"currency":"SEK","stage":"in_negotiation","status":"lost","owner_id":"01a14eb3-c634-7238-bc76-18b09b287f98","account_id":"01a15a77-0d3b-7e21-a6f9-3c8e2b1d4a05","reason":"Valde en lokal leverantör med lägre pris"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"room.created":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Deal rooms","x-module":"proposals","summary":"Deal room created","description":"A deal room was created. It is a draft — not visible to buyers — until `room.published`.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["room.created"]},"data":{"$ref":"#/components/schemas/WebhookRoomPayload"}}}]},"example":{"event":"room.created","event_id":"evt_1b8f3c6d-4a2e-4e71-b9d0-6f3a2c8e5d14","created":1792314845,"data":{"id":"01a1ba10-5e7c-7b88-9f02-1d4c6a8e3b29","slug":"k7m2xq9p","title":"Byggpartner Norden AB – serviceavtal","status":"draft","deal_id":"01a1b9f2-2c6e-7f40-8d13-6b0e4a9c5f17","published_at":null}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"room.published":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Deal rooms","x-module":"proposals","summary":"Deal room published","description":"A deal room was published: invited buyers can now open it.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["room.published"]},"data":{"$ref":"#/components/schemas/WebhookRoomPayload"}}}]},"example":{"event":"room.published","event_id":"evt_8e2d5f07-3b1c-4a96-8d4e-0c7b9a2f6e31","created":1792314845,"data":{"id":"01a1ba10-5e7c-7b88-9f02-1d4c6a8e3b29","slug":"k7m2xq9p","title":"Byggpartner Norden AB – serviceavtal","status":"published","deal_id":"01a1b9f2-2c6e-7f40-8d13-6b0e4a9c5f17","published_at":"2026-10-18T09:14:05+00:00"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"stakeholder.added":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Deal rooms","x-module":"proposals","summary":"Stakeholder added to a deal room","description":"A person on the buyer's side was invited to a deal room. Sent once per person and room; inviting the same email address again sends nothing.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["stakeholder.added"]},"data":{"$ref":"#/components/schemas/WebhookStakeholderPayload"}}}]},"example":{"event":"stakeholder.added","event_id":"evt_2c7a9e41-5d8f-4b03-a6e2-9f1d4c7b0e58","created":1792314845,"data":{"id":"01a1ba44-8c21-7f6d-b3a9-0e5d2f7c1b86","deal_room_id":"01a1ba10-5e7c-7b88-9f02-1d4c6a8e3b29","email":"erik.nilsson@byggpartner.se","name":"Erik Nilsson","role":"economic"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"message.posted":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Deal rooms","x-module":null,"summary":"Chat message posted","description":"A message was posted in a chat: a deal room's chat between buyer and seller (`deal_room_id` set), or a document's internal team chat (`document_id` set). `author_type` is `stakeholder` for someone on the buyer's side and `user` for a member of your organization.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["message.posted"]},"data":{"$ref":"#/components/schemas/WebhookMessagePayload"}}}]},"example":{"event":"message.posted","event_id":"evt_6f1e3b8a-9c2d-4e75-b0a4-3d8c6e1f9b72","created":1792314845,"data":{"id":"01a1bd07-4e96-7b2c-8f31-5a0d7e2c9b14","thread_id":"01a1ba12-0f4d-7c63-a8e7-2b9f5d1c6e30","deal_room_id":"01a1ba10-5e7c-7b88-9f02-1d4c6a8e3b29","document_id":null,"author_type":"stakeholder","author_id":"01a1ba44-8c21-7f6d-b3a9-0e5d2f7c1b86","author_name":"Erik Nilsson","body":"Hej! Kan ni lägga till fönsterputs två gånger per år i avtalet?","created_at":"2026-10-18T09:14:05+00:00"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"quote.accepted":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Quotes","x-module":"proposals","summary":"Quote accepted","description":"The buyer accepted a quote in the deal room. `total_cents` is the accepted total including VAT.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["quote.accepted"]},"data":{"$ref":"#/components/schemas/WebhookQuotePayload"}}}]},"example":{"event":"quote.accepted","event_id":"evt_0a5d8c3f-1e6b-4927-8c4d-7b2e0f5a9d13","created":1792314845,"data":{"id":"01a1bb02-7a3c-7e15-9d48-6c1f0b8e2a57","quote_number":"Q-2026-0142","title":"Fönsterbyte Solna – offert","deal_id":"01a1b9f2-2c6e-7f40-8d13-6b0e4a9c5f17","deal_room_id":"01a1ba10-5e7c-7b88-9f02-1d4c6a8e3b29","total_cents":18450000,"currency":"SEK","accepted_at":"2026-10-18T09:14:05+00:00"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"signature.requested":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Documents and e-signing","x-module":null,"summary":"Document sent for signing","description":"A document was sent for signing. `recipients` lists everyone it was sent to, in signing order: `signer` and `approver` must sign, `cc` and `viewer` only receive it.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["signature.requested"]},"data":{"$ref":"#/components/schemas/WebhookSignatureRequestedPayload"}}}]},"example":{"event":"signature.requested","event_id":"evt_4b9e2d7c-8f1a-4c36-9e5b-0d3a7c2f8e41","created":1792314845,"data":{"id":"01a1bc5d-93e4-7a10-b6c2-7e1f4d8a2c63","document_id":"01a1bc31-6f2a-7d84-9e15-3b8c0d4f7a29","deal_room_id":"01a1ba10-5e7c-7b88-9f02-1d4c6a8e3b29","title":"Serviceavtal 2027 – Byggpartner Norden AB","status":"sent","sent_at":"2026-10-18T09:14:05+00:00","expires_at":"2026-11-17T23:00:00+00:00","recipients":[{"id":"01a1bc5e-1d7a-7f32-8b64-9c0e3a5f7d28","name":"Erik Nilsson","email":"erik.nilsson@byggpartner.se","role":"signer"},{"id":"01a1bc5e-1d7b-7a09-a3c5-2f8d6b0e4c91","name":"Maria Ekström","email":"maria.ekstrom@byggpartner.se","role":"cc"}]}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"document.viewed":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Documents and e-signing","x-module":null,"summary":"Document opened by a recipient","description":"A recipient opened a document sent to them for signing, for the first time. Sent once per recipient — later views send nothing.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["document.viewed"]},"data":{"$ref":"#/components/schemas/WebhookDocumentViewedPayload"}}}]},"example":{"event":"document.viewed","event_id":"evt_9d3c6a1e-2b7f-4e80-a5d9-8c1b4e7f0a36","created":1792314845,"data":{"signature_request_id":"01a1bc5d-93e4-7a10-b6c2-7e1f4d8a2c63","document_id":"01a1bc31-6f2a-7d84-9e15-3b8c0d4f7a29","deal_room_id":"01a1ba10-5e7c-7b88-9f02-1d4c6a8e3b29","title":"Serviceavtal 2027 – Byggpartner Norden AB","recipient":{"id":"01a1bc5e-1d7a-7f32-8b64-9c0e3a5f7d28","name":"Erik Nilsson","email":"erik.nilsson@byggpartner.se","role":"signer"},"opened_at":"2026-10-18T09:14:05+00:00"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"signer.signed":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Documents and e-signing","x-module":null,"summary":"Recipient signed","description":"One recipient signed. When the last required signature is in, `signature.completed` follows.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["signer.signed"]},"data":{"$ref":"#/components/schemas/WebhookSignerSignedPayload"}}}]},"example":{"event":"signer.signed","event_id":"evt_e5a1c8f3-6d2b-4097-b4e1-7a9c3d5f2b08","created":1792314845,"data":{"signature_request_id":"01a1bc5d-93e4-7a10-b6c2-7e1f4d8a2c63","document_id":"01a1bc31-6f2a-7d84-9e15-3b8c0d4f7a29","deal_room_id":"01a1ba10-5e7c-7b88-9f02-1d4c6a8e3b29","title":"Serviceavtal 2027 – Byggpartner Norden AB","recipient":{"id":"01a1bc5e-1d7a-7f32-8b64-9c0e3a5f7d28","name":"Erik Nilsson","email":"erik.nilsson@byggpartner.se","role":"signer"},"signed_at":"2026-10-18T09:14:05+00:00"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"signature.completed":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Documents and e-signing","x-module":null,"summary":"Document fully signed","description":"Every recipient who had to sign has signed. The signed document can now be downloaded through the documents endpoints with `document_id`.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["signature.completed"]},"data":{"$ref":"#/components/schemas/WebhookSignatureCompletedPayload"}}}]},"example":{"event":"signature.completed","event_id":"evt_9a0d4e7b-2f61-4c3a-8b5e-1d7f9c2a6e04","created":1792314845,"data":{"id":"01a1bc5d-93e4-7a10-b6c2-7e1f4d8a2c63","document_id":"01a1bc31-6f2a-7d84-9e15-3b8c0d4f7a29","deal_room_id":"01a1ba10-5e7c-7b88-9f02-1d4c6a8e3b29","title":"Serviceavtal 2027 – Byggpartner Norden AB","status":"completed","completed_at":"2026-10-18T09:14:05+00:00"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"signature.canceled":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Documents and e-signing","x-module":null,"summary":"Signing canceled","description":"The sender canceled a signing before it was completed; the recipients' links stop working. `reason` is what the sender wrote, or null.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["signature.canceled"]},"data":{"$ref":"#/components/schemas/WebhookSignatureCanceledPayload"}}}]},"example":{"event":"signature.canceled","event_id":"evt_b7f2e9d4-0a3c-4e61-8f2d-5c9a1b7e3d06","created":1792314845,"data":{"id":"01a1bc5d-93e4-7a10-b6c2-7e1f4d8a2c63","document_id":"01a1bc31-6f2a-7d84-9e15-3b8c0d4f7a29","deal_room_id":"01a1ba10-5e7c-7b88-9f02-1d4c6a8e3b29","title":"Serviceavtal 2027 – Byggpartner Norden AB","status":"canceled","reason":"Fel prisbilaga, skickas om","canceled_at":"2026-10-18T09:14:05+00:00"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"work_order.completed":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Booking","x-module":"booking","summary":"Work order completed","description":"A work order was completed — in the field (`POST /work-orders/{workOrder}/complete`) or by the office (`POST /work-orders/{workOrder}/status` with `completed`): the checklist is done and the times are recorded. `customer_signed_at` is set when the customer signed for the work on site. This is the moment the job can be invoiced.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["work_order.completed"]},"data":{"$ref":"#/components/schemas/WebhookWorkOrderPayload"}}}]},"example":{"event":"work_order.completed","event_id":"evt_c4d8a2f6-7e1b-4359-a0c7-3f6e9b2d5a18","created":1792314845,"data":{"id":"01a1c0aa-3f5d-7b41-9e28-0c6a4d8f2b73","order_number":"AO-10482","title":"Fönsterputs, Kungsgatan 12","status":"completed","account_id":"01a15a77-0d3b-7e21-a6f9-3c8e2b1d4a05","agreed_price_cents":450000,"currency":"SEK","actual_start":"2026-10-18T06:30:00+00:00","actual_end":"2026-10-18T09:05:00+00:00","completed_at":"2026-10-18T09:14:05+00:00","customer_signed_at":"2026-10-18T09:12:40+00:00"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"invoice.issued":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Invoicing","x-module":"invoicing","summary":"Invoice issued","description":"An invoice or credit note was issued: it has its number and OCR reference and has been sent to the customer. `type` is `credit_note` for a credit note, which points at the invoice it credits through `credited_invoice_id`. Sent only once the issue is committed, so the number is final.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["invoice.issued"]},"data":{"$ref":"#/components/schemas/WebhookInvoicePayload"}}}]},"example":{"event":"invoice.issued","event_id":"evt_f1b6d3a9-5c8e-4a27-9d0f-2e7b4c1a8d65","created":1792314845,"data":{"id":"01a1c1d4-6b2e-7c90-8a35-4f1d7e0b9c26","invoice_number":"1042","series":"A","type":"invoice","status":"sent","account_id":"01a15a77-0d3b-7e21-a6f9-3c8e2b1d4a05","credited_invoice_id":null,"currency":"SEK","issue_date":"2026-10-18","due_date":"2026-11-17","net_cents":360000,"vat_cents":90000,"total_cents":450000,"ocr_reference":"10424"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"work_order.created_from_quote":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Booking","x-module":"booking","summary":"Work order created from a quote","description":"An accepted quote became a work order: automatically when the buyer accepted it (`created_automatically` true), or when somebody pressed *Create job* on the quote or called `POST /quotes/{quote}/convert`. Sent only to organizations with the scheduling module and the quote-to-job capability (`proposals.convert`, proposals Väx and Pro).\n\nThe work order is `booked` with no time on it — waiting to be planned — for the quote's customer, with the lines the buyer accepted (optional lines only if they ticked them) at the quoted quantities, prices, discounts and VAT rates, and the quote's ROT/RUT choice. `warnings` lists what could not be filled in: `service_address_missing` or `service_address_ambiguous` when the customer has no service address or several and none is the default (the work order then has none), `recurring_lines` when lines charged monthly or yearly were carried over once, `no_labour_lines` when the quote had only materials, and `no_lines` when it had none. Each quote becomes at most one work order, so this is sent once per quote.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["work_order.created_from_quote"]},"data":{"$ref":"#/components/schemas/WebhookWorkOrderFromQuotePayload"}}}]},"example":{"event":"work_order.created_from_quote","event_id":"evt_5a9c2e71-3b8d-4f06-9e14-7d2c6b0a8f53","created":1792314845,"data":{"id":"01a1c0b2-7d4e-7a19-8c35-2f9e6b1d4a07","order_number":"AO-10483","title":"Fönsterbyte Solna – offert","status":"booked","account_id":"01a15a77-0d3b-7e21-a6f9-3c8e2b1d4a05","contact_id":"01a15a79-4c1e-7f02-b8d7-6e3a9c2f1b48","service_address_id":null,"rot_rut":"rot","currency":"SEK","quote_id":"01a1bf90-2c6a-7e45-9d18-4b7f3a0e6c21","quote_number":"Q-2026-0142","created_automatically":true,"warnings":["service_address_missing"],"created_at":"2026-10-18T09:14:05+00:00"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"invoice.created_from_quote":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Invoicing","x-module":"invoicing","summary":"Invoice draft created from a quote","description":"An accepted quote became an invoice draft. Sent to organizations with the invoicing module but not the scheduling module — where there is a schedule, the quote becomes a work order instead and the invoice is written from the finished job — and with the quote-to-job capability (`proposals.convert`, proposals Väx and Pro): automatically when the buyer accepted (`created_automatically` true), or when somebody pressed *Create invoice draft* or called `POST /quotes/{quote}/convert`.\n\nThe draft has no number and has not been sent: issuing it is always a person's decision. It carries the lines the buyer accepted with their quantities, prices, discounts and VAT rates, and the quote's ROT/RUT choice. `warnings` lists what could not be filled in: `deduction_not_included` when the quote estimated ROT or RUT and the invoicing tier cannot claim it (the draft then has none), `tax_profile_missing` when the buyer the deduction is for could not be told from the customer, `recurring_lines` when lines charged monthly or yearly were carried over once, and `no_lines` when the quote had none. Each quote becomes at most one draft, so this is sent once per quote; `invoice.issued` follows when the draft is sent.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["invoice.created_from_quote"]},"data":{"$ref":"#/components/schemas/WebhookInvoiceFromQuotePayload"}}}]},"example":{"event":"invoice.created_from_quote","event_id":"evt_8e3b7d20-6f4a-4c19-a2d5-1b9c0e7f6a34","created":1792314845,"data":{"id":"01a1c1e8-5a3d-7b62-9f40-8c2e1d7b0a95","status":"draft","account_id":"01a15a77-0d3b-7e21-a6f9-3c8e2b1d4a05","contact_id":null,"currency":"SEK","net_cents":1476000,"vat_cents":369000,"total_cents":1845000,"rot_rut":"rot","rot_rut_requested_cents":396000,"quote_id":"01a1bf90-2c6a-7e45-9d18-4b7f3a0e6c21","quote_number":"Q-2026-0142","created_automatically":true,"warnings":[],"created_at":"2026-10-18T09:14:05+00:00"}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}},"ping":{"post":{"tags":["Webhook events"],"x-group":"Webhooks","x-category":"Testing","x-module":null,"summary":"Test event","description":"Sent only when you call POST /webhooks/{endpoint}/test, and only to that endpoint. Not something an endpoint subscribes to. Signed and retried like every other event, so it exercises your whole receiver.","parameters":[{"$ref":"#/components/parameters/WebhookSignatureHeader"},{"$ref":"#/components/parameters/WebhookEventHeader"},{"$ref":"#/components/parameters/WebhookEventIdHeader"},{"$ref":"#/components/parameters/WebhookDeliveryIdHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEvent"},{"type":"object","properties":{"event":{"type":"string","enum":["ping"]},"data":{"$ref":"#/components/schemas/WebhookPingPayload"}}}]},"example":{"event":"ping","event_id":"evt_c2e81f5a-7b3d-4f90-a6d2-5e8b1c4f7a39","created":1792315231,"data":{"endpoint_id":"01a1c3e0-4b2f-7d19-9c55-2f6a8e1b7d30","message":"A test event from Closepact. Your endpoint received and can verify it."}}}}},"responses":{"2XX":{"$ref":"#/components/responses/WebhookAcknowledged"}}}}},"components":{"securitySchemes":{"ApiKey":{"type":"http","scheme":"bearer","bearerFormat":"cp_live_…","description":"Send the key in the `Authorization` header: `Authorization: Bearer cp_live_…`. Keys are created under Settings → API keys and shown once."}},"headers":{"RateLimitLimit":{"description":"Requests this key may make per minute.","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current minute.","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds until the key may call again.","schema":{"type":"integer"}}},"parameters":{"Cursor":{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from `meta.next_cursor` or `meta.prev_cursor` of the previous page.","schema":{"type":"string"}},"PerPage":{"name":"per_page","in":"query","required":false,"description":"Items per page.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},"ContactId":{"name":"contact","in":"path","required":true,"description":"The contact's id.","schema":{"type":"string","format":"uuid"}},"DealId":{"name":"deal","in":"path","required":true,"description":"The deal's id.","schema":{"type":"string","format":"uuid"}},"SalesLimit":{"name":"limit","in":"query","required":false,"description":"Items per page. Values outside 1–100 are clamped.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},"DocumentId":{"name":"document","in":"path","required":true,"description":"The document's id.","schema":{"type":"string","format":"uuid"},"example":"01a15a2e-4b7c-7d12-9f3a-5c8e2d1b0a47"},"InvoiceId":{"name":"invoice","in":"path","required":true,"description":"The invoice's or credit note's id.","schema":{"type":"string","format":"uuid"}},"ProductId":{"name":"product","in":"path","required":true,"description":"The product's id.","schema":{"type":"string","format":"uuid"}},"ProposalId":{"name":"proposal","in":"path","required":true,"description":"The proposal's id.","schema":{"type":"string","format":"uuid"}},"QuoteId":{"name":"quote","in":"path","required":true,"description":"The quote's id.","schema":{"type":"string","format":"uuid"}},"RoomId":{"name":"room","in":"path","required":true,"description":"The room's id.","schema":{"type":"string","format":"uuid"}},"SignatureRequestId":{"name":"signature","in":"path","required":true,"description":"The signature request's id — `active_send.id` on a document, or `id` in the `signature.completed` webhook.","schema":{"type":"string","format":"uuid"},"example":"01a15a31-0c1f-7b90-a2e4-3d7c9b5f8e16"},"WebhookEndpointId":{"name":"endpoint","in":"path","required":true,"description":"The webhook endpoint's id.","schema":{"type":"string","format":"uuid"}},"WebhookSignatureHeader":{"name":"X-Signature","in":"header","required":true,"description":"`t=<timestamp>, v1=<signature>`: HMAC-SHA256 of `{t}.{raw body}` keyed with the endpoint's signing secret, hex-encoded. Verify it, and that `t` is within 300 seconds of now, before trusting the request.","schema":{"type":"string"},"example":"t=1792314846, v1=fa94cda11bea7d9dd2b7056fb63c376231e5a50275df0544f4aec2adcb461fa1"},"WebhookEventHeader":{"name":"X-Event","in":"header","required":true,"description":"The event name, the same as `event` in the body — for routing before parsing.","schema":{"type":"string"},"example":"deal.won"},"WebhookEventIdHeader":{"name":"X-Event-Id","in":"header","required":true,"description":"The event id, the same as `event_id` in the body. Identical for every endpoint that receives this event and for every retry.","schema":{"type":"string"},"example":"evt_3f9b2c1e-8a4d-4e6f-9b21-7c5d0e8a1f42"},"WebhookDeliveryIdHeader":{"name":"X-Delivery-Id","in":"header","required":true,"description":"This delivery's id — one per event per endpoint, unchanged across retries. Matches `id` in the endpoint's deliveries list.","schema":{"type":"string","format":"uuid"},"example":"01a1c412-77d0-7a3e-b1c4-5e9f0a2d6c81"}},"schemas":{"Error":{"type":"object","description":"Every error has a human-readable `message` and a stable machine-readable `error` code.","required":["message","error"],"properties":{"message":{"type":"string","description":"What went wrong, for a person."},"error":{"type":"string","description":"Stable error code, for code. See the Errors guide."},"required":{"description":"For `insufficient_scope`, the scope the key lacks; for `permission_denied`, the permissions any one of which would do.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"retry_after":{"type":"integer","description":"For `rate_limited`: seconds until the next request is allowed."},"module":{"type":["string","null"],"description":"For `module_seat_required`, the module the member the key acts as holds no seat in."}},"additionalProperties":false},"ValidationError":{"type":"object","required":["message","error","errors"],"properties":{"message":{"type":"string"},"error":{"type":"string","enum":["validation_failed"]},"errors":{"type":"object","description":"Field name → list of problems with that field.","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":false},"ModuleLockedError":{"type":"object","required":["message","error"],"properties":{"message":{"type":"string"},"error":{"type":"string","enum":["module_locked","module_read_only","seat_limit_reached","quota_exceeded"]},"module":{"type":["string","null"],"enum":["proposals","booking","invoicing",null]},"feature":{"type":["string","null"]},"current_tier":{"type":["string","null"]},"required_tier":{"type":["string","null"]},"seats":{"type":"integer"},"needed":{"type":"integer"},"metric":{"type":"string"},"limit":{"type":"integer"},"resets_at":{"type":["string","null"]}},"additionalProperties":false},"CursorLinks":{"type":"object","required":["first","last","prev","next"],"properties":{"first":{"type":["string","null"]},"last":{"type":["string","null"]},"prev":{"type":["string","null"],"description":"URL of the previous page, or null."},"next":{"type":["string","null"],"description":"URL of the next page, or null on the last page."}},"additionalProperties":false},"CursorMeta":{"type":"object","required":["path","per_page","next_cursor","prev_cursor"],"properties":{"path":{"type":"string"},"per_page":{"type":"integer"},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` to fetch the next page."},"prev_cursor":{"type":["string","null"]}},"additionalProperties":false},"UserSummary":{"type":"object","description":"A member of the organization, as shown on records they own or created.","required":["id","name","email"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string","format":"email"},"avatar_url":{"type":["string","null"]}},"additionalProperties":false},"Me":{"type":"object","required":["api_key","organization","acting_as","modules"],"properties":{"api_key":{"type":"object","required":["id","name","prefix","scopes","rate_limit_per_minute","expires_at"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"prefix":{"type":"string","description":"The first characters of the key, for telling keys apart."},"scopes":{"type":"array","items":{"type":"string","enum":["read","write","admin"]}},"rate_limit_per_minute":{"type":"integer"},"expires_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"organization":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}},"additionalProperties":false},"acting_as":{"type":"object","description":"The member who created the key. The key may do what this member may do, narrowed by its scopes.","required":["id","name","email"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string","format":"email"}},"additionalProperties":false},"modules":{"type":"array","description":"Modules the organization can use (writable) or still read after they lapsed.","items":{"type":"object","required":["key","tier","status","writable","has_seat"],"properties":{"key":{"type":"string","enum":["proposals","booking","invoicing"]},"tier":{"type":["string","null"],"enum":["start","grow","pro",null]},"status":{"type":["string","null"]},"writable":{"type":"boolean"},"has_seat":{"type":"boolean","description":"Whether the member the key acts as holds a seat in this module. Seats are per module; without one, the module's operations answer `403` `module_seat_required`."}},"additionalProperties":false}}},"additionalProperties":false},"Article":{"type":"object","description":"An article in the price list.","required":["id","article_number","name","description","type","unit","pricing","account_code","rot_rut_eligible","deductible","line_type","is_active","metadata","created_at","updated_at","deleted_at"],"properties":{"id":{"type":"string","format":"uuid"},"article_number":{"type":"string","description":"Artikelnummer, unique in the organization."},"name":{"type":"string","description":"Becomes the line's description."},"description":{"type":["string","null"]},"type":{"type":"string","enum":["goods","service","hour","fee"]},"unit":{"type":["string","null"],"description":"e.g. `tim`, `st`, `kvm`."},"pricing":{"type":"object","required":["unit_price_cents","currency","vat_rate"],"properties":{"unit_price_cents":{"type":"integer","description":"Öre, excluding VAT."},"currency":{"type":"string"},"vat_rate":{"type":"number","description":"Moms in percent: 25, 12, 6 or 0."}},"additionalProperties":false},"account_code":{"type":["string","null"],"description":"BAS sales account."},"rot_rut_eligible":{"type":"boolean","description":"Only ever true for `hour` and `service` articles."},"deductible":{"type":"boolean","description":"Whether a line built from it counts towards a ROT/RUT deduction: eligible *and* labour. The field to rely on."},"line_type":{"type":"string","enum":["goods","service","labour","fee"],"description":"What an invoice line built from it becomes."},"is_active":{"type":"boolean","description":"False once deactivated; still resolvable, no longer offered."},"metadata":{"type":["object","array","null"],"description":"Free-form; an empty list when unused.","additionalProperties":true},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"},"deleted_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"ArticleFields":{"type":"object","description":"The writable fields of an article. On update every field is optional.","properties":{"article_number":{"type":"string","maxLength":32},"name":{"type":"string","maxLength":200},"description":{"type":["string","null"],"maxLength":2000},"type":{"type":"string","enum":["goods","service","hour","fee"],"default":"goods"},"unit":{"type":["string","null"],"maxLength":32},"unit_price_cents":{"type":"integer","minimum":0,"default":0,"description":"Öre, excluding VAT."},"currency":{"type":"string","minLength":3,"maxLength":3,"default":"SEK"},"vat_rate":{"type":"number","enum":[25,12,6,0],"default":25},"account_code":{"type":["string","null"],"maxLength":8},"rot_rut_eligible":{"type":"boolean","default":false,"description":"Kept only for `hour` and `service` articles."},"is_active":{"type":"boolean","default":true},"sub_entity_id":{"type":["string","null"],"format":"uuid"},"metadata":{"type":"object","additionalProperties":true}},"additionalProperties":false},"ArticleDeletion":{"type":"object","required":["deleted","deactivated","issued_invoice_lines","reason"],"properties":{"deleted":{"type":"boolean"},"deactivated":{"type":"boolean","description":"True when the article was kept and deactivated instead."},"issued_invoice_lines":{"type":"integer","description":"How many issued invoice lines use it."},"reason":{"type":["string","null"]}},"additionalProperties":false},"Contact":{"type":"object","required":["id","name","email","title","phone","account","documents_count","rooms_count","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":["string","null"],"format":"email","description":"Null only for contacts synced from a CRM without an address."},"title":{"type":["string","null"],"description":"Job title."},"phone":{"type":["string","null"]},"account":{"type":["object","null"],"description":"The customer the contact works for, if any.","required":["id","name"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}},"additionalProperties":false},"documents_count":{"type":"integer","minimum":0,"description":"Signature requests in this organization with a signer at this e-mail."},"rooms_count":{"type":"integer","minimum":0,"description":"Deal rooms in this organization with a stakeholder at this e-mail."},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"ContactEnvelope":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Contact"}},"additionalProperties":false},"Customer":{"type":"object","description":"A customer in the register, shared by every module.","required":["id","name","customer_number","customer_type","org_number","vat_number","domain","industry","industry_code","country","invoicing","locale","notes","sub_entity_id","addresses","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"customer_number":{"type":["string","null"],"description":"Kundnummer, unique in the organization. Assigned on creation unless you send one. Null only on old records that came in through a CRM sync."},"customer_type":{"type":"string","enum":["company","private"],"description":"RUT applies only to private persons; ROT to private persons who own the home."},"org_number":{"type":["string","null"],"description":"Organisationsnummer, e.g. `769612-3456`. Empty for a private person."},"vat_number":{"type":["string","null"],"description":"VAT registration number (momsregistreringsnummer), e.g. `SE769612345601`. Needed on an EU or reverse-charge invoice."},"domain":{"type":["string","null"]},"industry":{"type":["string","null"]},"industry_code":{"type":["string","null"],"description":"SNI code."},"country":{"type":["string","null"],"description":"ISO 3166-1 alpha-2."},"invoicing":{"type":"object","description":"Defaults for the customer's invoices.","required":["payment_terms_days","late_interest_percent","vat_mode","delivery","peppol_id","reference","reminder_email","currency","credit_limit_cents","is_blocked"],"properties":{"payment_terms_days":{"type":"integer","description":"Betalningsvillkor in days. A new invoice takes these unless it states its own."},"late_interest_percent":{"type":["number","null"],"description":"Agreed dröjsmålsränta per year. Printed on the invoice and used for reminder interest; null means the statutory rate."},"vat_mode":{"type":"string","enum":["se","reverse","eu","export"],"description":"`se` Swedish VAT, `reverse` omvänd skattskyldighet within Sweden (e.g. construction services), `eu` intra-EU business sale, `export` outside the EU. Recorded for the people preparing invoices — line VAT is not changed from it, so put `vat_rate: 0` on the lines of a reverse-charge, EU or export invoice."},"delivery":{"type":"string","enum":["email","peppol","print"],"description":"How the customer wants invoices. Used by recurring invoices; a draft created through the API defaults to `email` unless you send `delivery_method`."},"peppol_id":{"type":["string","null"],"description":"Peppol participant id, e.g. `0007:7696123456`."},"reference":{"type":["string","null"],"description":"The customer's own reference for e-invoices (written `einvoice_reference`). Recurring invoices use it as *Er referens*."},"reminder_email":{"type":["string","null"],"description":"Where invoices and reminders go when the invoice names no contact with an email address."},"currency":{"type":"string","description":"ISO 4217. New invoices take it."},"credit_limit_cents":{"type":["integer","null"],"description":"Kreditgräns in öre. Informational."},"is_blocked":{"type":"boolean","description":"Set by your office, typically after a payment that never came. Informational: invoices are not refused on it."}},"additionalProperties":false},"locale":{"type":"string","description":"The language the customer's people are written to in — invoices, payment reminders, quotes and signing mails: `sv` or `en`. Any other value falls back to the organization's language, then Swedish."},"notes":{"type":["string","null"]},"sub_entity_id":{"type":["string","null"],"format":"uuid","description":"The office or legal entity the customer belongs to, for organizations that run several. Customers are not hidden by it."},"addresses":{"type":"array","items":{"$ref":"#/components/schemas/CustomerAddress"}},"contacts":{"type":"array","description":"Only on `GET /customers/{account}`.","items":{"$ref":"#/components/schemas/CustomerContact"}},"tax_profiles":{"type":"array","description":"Only on `GET /customers/{account}`.","items":{"$ref":"#/components/schemas/CustomerTaxProfile"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"CustomerFields":{"type":"object","description":"The writable fields of a customer. On update every field is optional.","properties":{"name":{"type":"string","maxLength":255},"customer_number":{"type":["string","null"],"maxLength":32,"description":"Leave out on create to have the next number assigned. Must be unique in the organization, deleted customers included — a deleted customer keeps its number. A number already taken is a 422 on `customer_number`."},"customer_type":{"type":"string","enum":["company","private"],"default":"company"},"org_number":{"type":["string","null"],"maxLength":32},"vat_number":{"type":["string","null"],"maxLength":32},"domain":{"type":["string","null"],"maxLength":255},"industry":{"type":["string","null"],"maxLength":64},"industry_code":{"type":["string","null"],"maxLength":16},"country":{"type":["string","null"],"minLength":2,"maxLength":2},"payment_terms_days":{"type":"integer","minimum":0,"maximum":365,"default":30},"late_interest_percent":{"type":["number","null"],"minimum":0,"maximum":100},"vat_mode":{"type":"string","enum":["se","reverse","eu","export"],"default":"se"},"invoice_delivery":{"type":"string","enum":["email","peppol","print"],"default":"email","description":"Returned as `invoicing.delivery`."},"peppol_id":{"type":["string","null"],"maxLength":64},"einvoice_reference":{"type":["string","null"],"maxLength":64,"description":"Returned as `invoicing.reference`."},"reminder_email":{"type":["string","null"],"format":"email","maxLength":320},"currency":{"type":"string","minLength":3,"maxLength":3,"default":"SEK"},"locale":{"type":"string","maxLength":8,"description":"`sv` or `en`. Leave out on create to take the organization's language, which is Swedish unless the organization chose English."},"credit_limit_cents":{"type":["integer","null"],"minimum":0},"is_blocked":{"type":"boolean","default":false},"notes":{"type":["string","null"]},"sub_entity_id":{"type":["string","null"],"format":"uuid"}},"additionalProperties":false},"CustomerAddress":{"type":"object","description":"A billing, postal or service address of a customer.","required":["id","organization_id","account_id","street","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"account_id":{"type":"string","format":"uuid","description":"The customer."},"type":{"type":"string","enum":["billing","postal","service"],"description":"`service` is where work is carried out."},"label":{"type":["string","null"]},"street":{"type":"string"},"street2":{"type":["string","null"]},"zip":{"type":["string","null"],"description":"Postnummer, e.g. `165 72`."},"city":{"type":["string","null"]},"country":{"type":"string","description":"ISO 3166-1 alpha-2; `SE` unless stated."},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"geofence_radius_m":{"type":["integer","null"],"description":"How close, in metres, a clock-in has to be to count as on site. Set to 100 when the address is given coordinates without one."},"access_notes":{"type":["string","null"],"description":"Door codes, where the key is kept — shown to field staff."},"contact_id":{"type":["string","null"],"format":"uuid","description":"The person to ask for at this address."},"is_default":{"type":"boolean"},"metadata":{"type":["object","array"],"description":"Free-form; an empty list when unused.","additionalProperties":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"deleted_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"CustomerAddressFields":{"type":"object","description":"The writable fields of an address. On update every field is optional.","properties":{"type":{"type":"string","enum":["billing","postal","service"],"default":"service"},"label":{"type":["string","null"],"maxLength":128},"street":{"type":"string","maxLength":255},"street2":{"type":["string","null"],"maxLength":255},"zip":{"type":["string","null"],"maxLength":16},"city":{"type":["string","null"],"maxLength":128},"country":{"type":"string","minLength":2,"maxLength":2,"default":"SE"},"lat":{"type":["number","null"],"minimum":-90,"maximum":90},"lng":{"type":["number","null"],"minimum":-180,"maximum":180},"geofence_radius_m":{"type":["integer","null"],"minimum":10,"maximum":5000},"access_notes":{"type":["string","null"]},"contact_id":{"type":["string","null"],"format":"uuid"},"is_default":{"type":"boolean","default":false}},"additionalProperties":false},"CustomerContact":{"type":"object","description":"A person at a customer, as stored. Managed through the contacts endpoints.","required":["id","organization_id","account_id","email","name","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"account_id":{"type":["string","null"],"format":"uuid"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"title":{"type":["string","null"]},"phone":{"type":["string","null"]},"crm_external_id":{"type":["string","null"],"description":"The contact's id in a connected CRM."},"crm_provider":{"type":["string","null"]},"metadata":{"type":["object","array","null"],"additionalProperties":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"deleted_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"CustomerTaxProfile":{"type":"object","description":"What a ROT or RUT claim needs from the buyer: who they are and, for ROT, which home. Read-only here: profiles are entered in the Closepact panel, where the full personnummer is stored encrypted and printed on the invoice. Through the API only its last four digits leave Closepact.","required":["id","organization_id","account_id","address_id","label","personal_number_last4","property_designation","apartment_number","brf_org_number","co_applicant_name","co_applicant_personal_number_last4","applicant_share_percent","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"account_id":{"type":"string","format":"uuid"},"address_id":{"type":["string","null"],"format":"uuid","description":"The service address the claim concerns."},"label":{"type":["string","null"]},"personal_number_last4":{"type":["string","null"],"description":"Last four digits of the buyer's personnummer."},"property_designation":{"type":["string","null"],"description":"Fastighetsbeteckning for ROT on a house, e.g. `Stockholm Solgläntan 3:14`."},"apartment_number":{"type":["string","null"],"description":"Lägenhetsnummer for ROT in a bostadsrätt."},"brf_org_number":{"type":["string","null"],"description":"The housing cooperative's organisationsnummer for ROT in a bostadsrätt."},"co_applicant_name":{"type":["string","null"],"description":"A second owner sharing the claim."},"co_applicant_personal_number_last4":{"type":["string","null"]},"applicant_share_percent":{"type":"number","description":"The buyer's share of the claim; 100 unless it is split with a co-applicant."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"deleted_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"DealStage":{"type":"string","description":"Where an open deal stands, in pipeline order: `draft`, `ready_to_send`, `sent`, `opened`, `engaged`, `in_negotiation`, `proposal_approved`, `contract_sent`, `contract_signed`.","enum":["draft","ready_to_send","sent","opened","engaged","in_negotiation","proposal_approved","contract_sent","contract_signed"]},"DealStatus":{"type":"string","description":"`open` while being worked; `won` or `lost` once closed; `archived` when put away in the panel.","enum":["open","won","lost","archived"]},"Deal":{"type":"object","required":["id","title","value_cents","currency","stage","status","expected_close_date","closed_at","lost_reason","owner","account","custom_fields","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"value_cents":{"type":"integer","minimum":0,"description":"The deal's value in the currency's minor unit (öre for SEK): 320000 is 3 200 kr."},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"ISO 4217 code, upper case.","example":"SEK"},"stage":{"$ref":"#/components/schemas/DealStage"},"status":{"$ref":"#/components/schemas/DealStatus"},"expected_close_date":{"type":["string","null"],"format":"date"},"closed_at":{"type":["string","null"],"format":"date-time","description":"When the deal was marked won or lost."},"lost_reason":{"type":["string","null"]},"owner":{"$ref":"#/components/schemas/UserSummary"},"account":{"type":["object","null"],"description":"The customer the deal is with, if any.","required":["id","name","domain"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"domain":{"type":["string","null"]}},"additionalProperties":false},"custom_fields":{"type":["object","array","null"],"maxItems":0,"description":"Free-form values your organization keeps on the deal — a good place for your own system's id. An empty set comes back as `[]`.","additionalProperties":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"DealEnvelope":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Deal"}},"additionalProperties":false},"DealCreate":{"type":"object","required":["title","value_cents","currency","stage"],"properties":{"title":{"type":"string","minLength":1,"maxLength":255},"value_cents":{"type":"integer","minimum":0,"description":"In the currency's minor unit (öre for SEK)."},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"ISO 4217 code. Stored upper case."},"stage":{"$ref":"#/components/schemas/DealStage"},"account_id":{"type":["string","null"],"format":"uuid","description":"A customer in this organization."},"owner_id":{"type":["string","null"],"format":"uuid","description":"An active member of this organization. Defaults to the member the key acts as."},"expected_close_date":{"type":["string","null"],"format":"date"},"custom_fields":{"type":["object","null"],"additionalProperties":true}},"additionalProperties":false},"DealUpdate":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":255},"value_cents":{"type":"integer","minimum":0},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"ISO 4217 code. Send it upper case: unlike on create, it is stored as sent."},"account_id":{"type":["string","null"],"format":"uuid","description":"A customer in this organization, or null to detach it."},"owner_id":{"type":"string","format":"uuid","description":"An active member of this organization."},"expected_close_date":{"type":["string","null"],"format":"date"},"custom_fields":{"type":["object","null"],"additionalProperties":true,"description":"Replaces the whole object."}},"additionalProperties":false},"SalesDeleted":{"type":"object","description":"Confirmation that the record was deleted.","required":["deleted"],"properties":{"deleted":{"type":"boolean","enum":[true]}},"additionalProperties":false},"Document":{"type":"object","description":"A file in Closepact, and where it stands with its recipients.","required":["id","title","type","mime_type","size_bytes","page_count","page_images_status","status","lifecycle_status","value_cents","currency","sent_at","expires_at","days_left","checksum_sha256","signer_otp_required","signer_verification_channel","bankid_sign_required","bankid_identification_required","metadata","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"type":{"type":"string","enum":["pdf","docx","image","video","file"],"description":"Derived from the detected MIME type on upload."},"mime_type":{"type":["string","null"]},"size_bytes":{"type":"integer"},"page_count":{"type":["integer","null"],"description":"Known once a PDF has been processed."},"page_images_status":{"type":["string","null"],"enum":["pending","ready","unavailable",null],"description":"Page images back the signing view and per-page statistics. `unavailable` for anything that is not a PDF."},"status":{"type":"string","enum":["processing","ready","failed","quarantined"],"description":"The file: `processing` after upload, then `ready`, or `failed` when the virus scan rejects it or processing gives up. `quarantined` is reserved."},"lifecycle_status":{"type":"string","enum":["draft","sent","opened","signed","declined","expired","canceled"],"description":"The document with its recipients. `draft` — never sent. `sent` — sent to recipients. `opened` — a recipient has opened it. `signed` — every signer and approver has signed (the `signature.completed` webhook fires). `expired` — `expires_at` passed before it was signed. `canceled` — its signature request was canceled. `declined` is reserved: a decline is recorded on the recipient and on `active_send.status`."},"value_cents":{"type":["integer","null"],"description":"What the document is worth, in the currency's minor unit (öre for SEK)."},"currency":{"type":"string","description":"ISO 4217 code.","example":"SEK"},"sent_at":{"type":["string","null"],"format":"date-time","description":"When it was first sent."},"expires_at":{"type":["string","null"],"format":"date-time"},"days_left":{"type":["integer","null"],"description":"Whole days until `expires_at`; negative once overdue; null without a deadline."},"checksum_sha256":{"type":["string","null"],"description":"SHA-256 of the uploaded file, hex."},"signer_otp_required":{"type":"boolean","description":"Signers confirm who they are with a six-digit code (valid 10 minutes, 5 tries) before they can sign or decline; the code being sent and entered are recorded in the audit trail. Sent by email, or by SMS when `signer_verification_channel` is `sms`. On by default for new documents; change it with `PATCH /documents/{document}`."},"signer_verification_channel":{"type":"string","enum":["email","sms"],"description":"Where the one-time code goes. `sms` sends it to the recipient's `phone_number`; a recipient without one, or a server without an SMS provider, gets it by email instead, and the signing page says so. Each SMS is billed as metered usage (0.89 kr)."},"bankid_sign_required":{"type":"boolean","description":"Signers and approvers sign with Swedish BankID instead of a typed or drawn signature. The BankID app shows the document's title and SHA-256; BankID's signature and OCSP response are kept as evidence and their hashes recorded in the audit trail. Each completed BankID signature is billed as metered usage (4.49 kr). Needs a tier with BankID (402 otherwise) and BankID configured on the server (503 `bankid_unavailable` otherwise)."},"bankid_identification_required":{"type":"boolean","description":"Every recipient identifies with BankID before the document is shown to them at all. Same requirements as `bankid_sign_required`."},"metadata":{"type":["object","array","null"],"description":"Facts recorded while processing the file. Sent as an empty array `[]` when there are none.","properties":{"original_name":{"type":"string","description":"The file name it was uploaded with."},"has_text":{"type":"boolean","description":"Whether text could be extracted."},"text_length":{"type":"integer"},"processed_at":{"type":"string","format":"date-time"},"infected":{"type":"boolean","description":"Set when the virus scan rejected the file."}},"additionalProperties":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"owner":{"type":["object","null"],"description":"Who uploaded it (for an upload through the API, the member the key acts as); null if that account no longer exists. Detail responses only.","required":["id","name"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}},"additionalProperties":false},"recipients":{"type":"array","description":"Everyone it has been sent to, in signing order. Detail and send responses only.","items":{"$ref":"#/components/schemas/DocumentRecipient"}},"active_send":{"type":["object","null"],"description":"The signature request currently in play, or null if it was never sent. Detail and send responses only. Full detail: `GET /signatures/{id}`.","required":["id","status","signing_order","sent_at","expires_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The signature request's id."},"status":{"type":"string","enum":["draft","sent","partially_signed","completed","declined","expired","canceled"]},"signing_order":{"type":"string","enum":["parallel","sequential"]},"sent_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"tags":{"type":"array","description":"Detail responses only.","items":{"type":"object","required":["id","slug","label","color"],"properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"label":{"type":"string"},"color":{"type":["string","null"]}},"additionalProperties":false}},"engagement":{"$ref":"#/components/schemas/DocumentEngagementSummary"}},"additionalProperties":false},"DocumentRecipient":{"type":"object","description":"Somebody a document was sent to.","required":["id","name","email","role","status","order_index","opened_at","signed_at","declined_at","decline_reason","view_count","total_time_ms","last_activity_at","reminder_count","last_reminded_at","phone_number","bankid_required","personal_number_masked"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["signer","approver","cc","viewer"],"description":"`signer` and `approver` must sign; `cc` and `viewer` only read."},"status":{"type":"string","enum":["pending","opened","signed","declined"],"description":"`pending` until they open their link, then `opened`, then `signed` or `declined`."},"order_index":{"type":"integer","description":"Position in a sequential signing order, from 0."},"opened_at":{"type":["string","null"],"format":"date-time"},"signed_at":{"type":["string","null"],"format":"date-time"},"declined_at":{"type":["string","null"],"format":"date-time"},"decline_reason":{"type":["string","null"]},"view_count":{"type":"integer"},"total_time_ms":{"type":"integer","description":"Time spent reading, in milliseconds."},"last_activity_at":{"type":["string","null"],"format":"date-time"},"reminder_count":{"type":"integer"},"last_reminded_at":{"type":["string","null"],"format":"date-time"},"phone_number":{"type":["string","null"],"description":"E.164, for the one-time code by SMS.","example":"+46701234567"},"bankid_required":{"type":"boolean","description":"This recipient signs with BankID even if the document does not ask it of everybody."},"personal_number_masked":{"type":["string","null"],"description":"The last four digits of the personal identity number BankID has to report for this recipient; any other person's BankID is refused. The full number is never returned.","example":"••••••••-1234"}},"additionalProperties":false},"DocumentEngagementSummary":{"type":"object","description":"How much a document has been read. Detail responses only.","required":["views","unique_readers","unique_sessions","total_time_ms","first_opened_at","last_activity_at","event_counts"],"properties":{"views":{"type":"integer","description":"Times it was opened."},"unique_readers":{"type":"integer","description":"Recipients who opened it."},"unique_sessions":{"type":"integer"},"total_time_ms":{"type":"integer"},"first_opened_at":{"type":["string","null"],"format":"date-time"},"last_activity_at":{"type":["string","null"],"format":"date-time"},"event_counts":{"type":["object","array"],"description":"Event type → count. Sent as an empty array `[]` when there are none.","additionalProperties":{"type":"integer"}}},"additionalProperties":false},"DocumentEngagement":{"type":"object","required":["summary","pages","recipients","event_counts"],"properties":{"summary":{"$ref":"#/components/schemas/DocumentEngagementSummary"},"pages":{"type":"array","description":"One entry per page, from page 1. Time per page is included from the Väx tier of proposals (`tracking.pages`); without it this is an empty array.","items":{"type":"object","required":["page_number","duration_ms","views"],"properties":{"page_number":{"type":"integer"},"duration_ms":{"type":"integer"},"views":{"type":"integer"}},"additionalProperties":false}},"recipients":{"type":"array","items":{"type":"object","required":["recipient_id","name","email","role","status","views","total_time_ms","last_activity_at"],"properties":{"recipient_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["signer","approver","cc","viewer"]},"status":{"type":"string","enum":["pending","opened","signed","declined"]},"views":{"type":"integer"},"total_time_ms":{"type":"integer"},"last_activity_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false}},"event_counts":{"type":["object","array"],"description":"Event type → count. Sent as an empty array `[]` when there are none.","additionalProperties":{"type":"integer"}}},"additionalProperties":false},"DocumentTimeline":{"type":"object","required":["events","by_recipient","integrity"],"properties":{"events":{"type":"array","description":"Newest first; at most 200.","items":{"$ref":"#/components/schemas/DocumentTimelineEvent"}},"by_recipient":{"type":"array","items":{"type":"object","required":["signer_id","name","event_count","first_seen","last_seen"],"properties":{"signer_id":{"type":"string","format":"uuid","description":"The recipient's id."},"name":{"type":["string","null"]},"event_count":{"type":"integer"},"first_seen":{"type":["string","null"],"format":"date-time"},"last_seen":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false}},"integrity":{"type":"object","description":"Whether the signature audit chains behind this document still verify.","required":["status","checked_at","requests"],"properties":{"status":{"type":"string","enum":["ok","broken","empty"],"description":"`broken` if any request's chain is broken; `ok` if at least one verifies and none is broken; `empty` when there is nothing to check."},"checked_at":{"type":"string","format":"date-time"},"requests":{"type":"array","description":"One report per signature request, newest first.","items":{"$ref":"#/components/schemas/SignatureChainReport"}}},"additionalProperties":false}},"additionalProperties":false},"DocumentTimelineEvent":{"type":"object","required":["id","source","event_type","actor","page_number","value","occurred_at"],"properties":{"id":{"type":"string"},"source":{"type":"string","enum":["engagement","signature"],"description":"`engagement`: something a recipient did with the document. `signature`: an entry in the signature audit trail."},"event_type":{"type":"string","example":"signer.signed"},"actor":{"type":["object","null"],"description":"Who did it: a recipient, or the member who sent the request. Null when unknown.","required":["type","id","name"],"properties":{"type":{"type":"string","enum":["recipient","user"]},"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]}},"additionalProperties":false},"page_number":{"type":["integer","null"]},"value":{"type":["object","array","null"],"description":"Event details, e.g. `{\"duration_ms\": 38000}` for `time.spent` or `{\"type\": \"drawn\"}` for `signer.signed`. Sent as `[]` when there are none.","additionalProperties":true},"occurred_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"DocumentDownloadLink":{"type":"object","required":["url","expires_in_seconds"],"properties":{"url":{"type":"string","format":"uri","description":"Signed URL to `GET` without an `Authorization` header. Works for `expires_in_seconds`."},"expires_in_seconds":{"type":"integer","description":"How long the URL works: 900 (15 minutes)."}},"additionalProperties":false},"DocumentReminderResult":{"type":"object","required":["sent","skipped"],"properties":{"sent":{"type":"integer","description":"Reminders emailed."},"skipped":{"type":"array","items":{"type":"object","required":["recipient_id","reason"],"properties":{"recipient_id":{"type":"string","format":"uuid"},"reason":{"type":"string","enum":["already_signed","declined","cooldown","document_expired","max_reminders_reached"],"description":"`cooldown`: reminded less than 60 minutes ago. `max_reminders_reached` only applies to automatic reminders."}},"additionalProperties":false}}},"additionalProperties":false},"Employee":{"type":"object","description":"Somebody who can be sent to a job (personal). Not the same as a member of the organization: an employee may be linked to a member's login (`user`) but need not be — field staff who never sign in are scheduled without using a licence.","required":["id","employee_number","first_name","last_name","name","email","phone","employment_type","weekly_hours","hourly_cost_cents","hourly_billing_rate_cents","color","status","started_at","ended_at","skills","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The employee id."},"employee_number":{"type":["string","null"],"description":"Your own number for them (e.g. from payroll), or null."},"first_name":{"type":"string","description":"First name."},"last_name":{"type":"string","description":"Last name."},"name":{"type":"string","description":"First and last name."},"email":{"type":["string","null"],"description":"Email address; assignments are emailed here. Null for none."},"phone":{"type":["string","null"],"description":"Phone number as entered; used for SMS notices when the organization has turned them on."},"employment_type":{"type":"string","enum":["employed","hourly","contractor"],"description":"Kind of employment."},"weekly_hours":{"type":["number","null"],"description":"Contracted hours per week, or null."},"hourly_cost_cents":{"type":["integer","null"],"description":"What an hour of this person costs the company, in öre (not what it is sold for). Copied onto time entries as `hourly_rate_cents` when they are approved, for margin; never invoiced."},"hourly_billing_rate_cents":{"type":["integer","null"],"description":"What an hour of this person is invoiced at, in öre excluding VAT. Copied onto time entries as `billing_rate_cents` when they are approved; the invoice basis bills hourly work at it unless the job was quoted one service price per hour. Null: such hours are billed at nothing and the basis warns."},"color":{"type":["string","null"],"description":"Colour on the board, e.g. `#2F80ED`, or null."},"status":{"type":"string","enum":["active","inactive"],"description":"Inactive employees stay on past jobs and timesheets but cannot be scheduled without a `not_employed` error."},"started_at":{"type":["string","null"],"format":"date","description":"First day of employment, or null. Calendar date, `YYYY-MM-DD`."},"ended_at":{"type":["string","null"],"format":"date","description":"Last day of employment, or null. Calendar date, `YYYY-MM-DD`."},"user":{"description":"The member login this employee is linked to, or null. Included by the list and `GET /employees/{employee}`.","oneOf":[{"type":"object","required":["id","name","email"],"properties":{"id":{"type":"string","format":"uuid","description":"The member (user) id."},"name":{"type":"string","description":"Name."},"email":{"type":"string","format":"email","description":"Login email."}},"additionalProperties":false},{"type":"null"}]},"skills":{"type":"array","description":"Competences the employee holds. A service that requires competences can only be done by somebody holding them all.","items":{"$ref":"#/components/schemas/EmployeeSkill"}},"availability":{"type":"array","description":"Weekly working hours. Included by `GET /employees/{employee}` only; also at `GET /employees/{employee}/availability`.","items":{"$ref":"#/components/schemas/AvailabilityRule"}},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"updated_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."}},"additionalProperties":false},"EmployeeSkill":{"type":"object","required":["id","name","color","level"],"properties":{"id":{"type":"string","format":"uuid","description":"The skill id (see `GET /skills`)."},"name":{"type":"string","description":"Skill name."},"color":{"type":["string","null"],"description":"Colour, or null."},"level":{"type":"integer","minimum":1,"maximum":5,"description":"How good they are at it, 1–5; 1 unless a level was set with `skills`."}},"additionalProperties":false},"Skill":{"type":"object","description":"A competence (kompetens) an employee can hold and a service can require.","required":["id","name","slug","color"],"properties":{"id":{"type":"string","format":"uuid","description":"The skill id, for `skill_ids` on employees and services."},"name":{"type":"string","description":"Name, e.g. \"Storstädning\"."},"slug":{"type":"string","description":"URL-friendly name, unique in the organization."},"color":{"type":["string","null"],"description":"Colour, or null."}},"additionalProperties":false},"AvailabilityRule":{"type":"object","description":"Hours an employee normally works on one weekday. Several rules on one day are allowed (a split shift around lunch). An employee with no rules at all is treated as always available.","required":["id","organization_id","employee_id","weekday","start_time","end_time","valid_from","valid_to","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The rule id."},"organization_id":{"type":"string","format":"uuid","description":"Your organization."},"employee_id":{"type":"string","format":"uuid","description":"The employee."},"weekday":{"type":"integer","description":"ISO weekday: 1 = Monday … 7 = Sunday.","minimum":1,"maximum":7},"start_time":{"type":"string","description":"Start, `HH:MM:SS`, in Swedish local time (`Europe/Stockholm`). Job times, which are UTC instants, are converted to that clock — and to its weekday — before they are compared with the rule."},"end_time":{"type":"string","description":"End, `HH:MM:SS`, after the start."},"valid_from":{"type":["string","null"],"format":"date-time","description":"First day the rule applies, as midnight UTC of that date (`2026-01-01T00:00:00.000000Z`), or null for always. Only the date counts: it is matched against the job's date in Swedish time."},"valid_to":{"type":["string","null"],"format":"date-time","description":"Last day the rule applies (the whole day), as midnight UTC of that date, or null for open-ended. Only the date counts: it is matched against the job's date in Swedish time."},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"updated_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."}},"additionalProperties":false},"InvoiceStatus":{"type":"string","enum":["draft","sent","partially_paid","paid","overdue","credited","cancelled","written_off"],"description":"See the status table in the Invoices section."},"Invoice":{"type":"object","description":"An invoice or credit note.","required":["id","type","invoice_number","series","reference","status","status_label","is_editable","handover","customer","dates","references","amounts","rot_rut","delivery_method","delivery","delivery_failure","sent_at","viewed_at","notes","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["invoice","credit_note"]},"invoice_number":{"type":["string","null"],"description":"Null on a draft; allocated at send."},"series":{"type":"string","description":"`A` unless chosen otherwise; credit notes `K` + the invoice's series."},"reference":{"type":"string","description":"The number as said out loud — `A-1042` — or `Utkast` for a draft."},"status":{"$ref":"#/components/schemas/InvoiceStatus"},"status_label":{"type":"string","description":"The status in Swedish, as printed: Utkast, Skickad, Delbetald, Betald, Förfallen, Krediterad, Makulerad, Avskriven."},"is_editable":{"type":"boolean","description":"True only for a draft."},"handover":{"description":"The accepted quote this invoice was drafted from — for an organization with invoicing and no scheduling — or null.","oneOf":[{"$ref":"#/components/schemas/QuoteHandoverOrigin"},{"type":"null"}]},"customer":{"type":"object","required":["id","name","customer_number","payment_terms_days"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"customer_number":{"type":["string","null"]},"payment_terms_days":{"type":"integer"}},"additionalProperties":false},"contact":{"description":"The person the invoice is addressed to. Only on a single invoice.","anyOf":[{"$ref":"#/components/schemas/CustomerContact"},{"type":"null"}]},"dates":{"type":"object","required":["issue_date","due_date","delivery_date","payment_terms_days","days_overdue"],"properties":{"issue_date":{"type":["string","null"],"format":"date","description":"Fakturadatum. Provisional on a draft; set at send."},"due_date":{"type":["string","null"],"format":"date","description":"Förfallodatum. Provisional on a draft; set at send."},"delivery_date":{"type":["string","null"],"format":"date","description":"Leveransdatum — decides the VAT period."},"payment_terms_days":{"type":"integer"},"days_overdue":{"type":"integer","description":"0 unless overdue with something still owed."}},"additionalProperties":false},"references":{"type":"object","required":["your_reference","our_reference","ocr_reference"],"properties":{"your_reference":{"type":["string","null"],"description":"Er referens."},"our_reference":{"type":["string","null"],"description":"Vår referens."},"ocr_reference":{"type":["string","null"],"description":"The payment reference the customer gives their bank: the invoice number's digits plus a Luhn check digit. Null on drafts and credit notes."}},"additionalProperties":false},"amounts":{"type":"object","description":"Integers in öre.","required":["currency","net_cents","vat_cents","rounding_cents","total_cents","paid_cents","outstanding_cents","vat_by_rate"],"properties":{"currency":{"type":"string"},"net_cents":{"type":"integer","description":"Excluding VAT. Negative on a credit note."},"vat_cents":{"type":"integer"},"rounding_cents":{"type":"integer","description":"Öresavrundning to whole kronor, between −50 and 50."},"total_cents":{"type":"integer","description":"Including VAT and rounding."},"paid_cents":{"type":"integer","description":"Sum of all payments."},"outstanding_cents":{"type":"integer","description":"What the customer still owes: total − ROT/RUT requested − paid, never below 0."},"vat_by_rate":{"type":"array","description":"Net and VAT per rate, as they have to be declared.","items":{"type":"object","required":["rate","net_cents","vat_cents"],"properties":{"rate":{"type":"number","description":"25, 12, 6 or 0."},"net_cents":{"type":"integer"},"vat_cents":{"type":"integer"}},"additionalProperties":false}}},"additionalProperties":false},"rot_rut":{"type":"object","description":"The tax deduction on the invoice, kept apart from what the customer owes.","required":["type","work_type","requested_cents","approved_cents","refused_cents","status","customer_pays_cents","annual_ceiling_applied","tax_profile_id","buyer"],"properties":{"type":{"type":"string","enum":["none","rot","rut"]},"work_type":{"$ref":"#/components/schemas/RotRutWorkType"},"requested_cents":{"type":"integer","description":"The deduction: 30 % (ROT) or 50 % (RUT) of the eligible labour including VAT. Claimed from Skatteverket."},"approved_cents":{"type":"integer","description":"What Skatteverket paid, once decided."},"refused_cents":{"type":"integer","description":"What Skatteverket refused. Owed by the customer again, and included in `amounts.outstanding_cents` from the moment the decision is recorded. 0 until then."},"status":{"type":"string","enum":["none","pending","submitted","approved","partially_approved","rejected"],"description":"Where the claim stands: `none` without a deduction, `pending` from send until the claim file for Skatteverket is produced, `submitted` after, then `approved`, `partially_approved` or `rejected` once Skatteverket's decision is recorded. If Skatteverket grants less than requested, the difference is owed by the customer."},"customer_pays_cents":{"type":"integer","description":"total − requested: what the customer transfers."},"annual_ceiling_applied":{"type":"boolean","enum":[false],"description":"Always false: the yearly ceiling per person depends on other companies' claims and is not checked."},"tax_profile_id":{"type":["string","null"],"format":"uuid","description":"The customer's ROT/RUT tax profile the deduction is claimed for (see `tax_profiles` on the customer). Sending an invoice with a deduction answers 422 naming the missing field when there is none, or when it lacks the personnummer or, for ROT, the fastighetsbeteckning or the housing association's organisation number and apartment number."},"buyer":{"type":["object","null"],"description":"The buyer as printed on the issued invoice, copied at send. Null on a draft and on an invoice without a deduction. The full personnummer is on the PDF only.","required":["name","personal_number_masked","property_designation","brf_org_number","apartment_number","work_address"],"properties":{"name":{"type":["string","null"]},"personal_number_masked":{"type":["string","null"],"description":"The last four digits, e.g. `••••••••-1234`."},"property_designation":{"type":["string","null"],"description":"Fastighetsbeteckning, for ROT on a house."},"brf_org_number":{"type":["string","null"],"description":"The housing association's organisation number, for ROT in a bostadsrätt."},"apartment_number":{"type":["string","null"],"description":"Lägenhetsnummer, for ROT in a bostadsrätt."},"work_address":{"type":["string","null"],"description":"Where the work was done."}},"additionalProperties":false}},"additionalProperties":false},"lines":{"type":"array","description":"Absent from lists.","items":{"$ref":"#/components/schemas/InvoiceLine"}},"delivery_method":{"type":"string","enum":["email","peppol","print"]},"delivery":{"type":["object","null"],"description":"Where Closepact emailed the invoice. `automatic` is true when a recurring invoice agreement sent it; `status` is `queued` for a send from the panel or API (handed to the mail queue) and `sent` for an automatic one (accepted by the mail server before the invoice counted as sent). Null when nothing was emailed — Peppol and print are not delivered by Closepact.","required":["method","to","at","automatic","status"],"properties":{"method":{"type":"string","enum":["email"]},"to":{"type":"string","format":"email"},"at":{"type":"string","format":"date-time"},"automatic":{"type":"boolean"},"status":{"type":"string","enum":["queued","sent"]}},"additionalProperties":false},"delivery_failure":{"type":["object","null"],"description":"Why a recurring invoice agreement that was meant to send this invoice did not. The invoice is then still a draft, without a number.","required":["at","reason","automatic"],"properties":{"at":{"type":"string","format":"date-time"},"reason":{"type":"string"},"automatic":{"type":"boolean"}},"additionalProperties":false},"sent_at":{"type":["string","null"],"format":"date-time"},"viewed_at":{"type":["string","null"],"format":"date-time","description":"When the customer first opened the emailed invoice."},"notes":{"type":["string","null"],"description":"Printed on the invoice. On a credit note, the reason."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"InvoiceFields":{"type":"object","description":"The writable header fields of an invoice draft. On update every field is optional.","properties":{"account_id":{"type":"string","format":"uuid","description":"The customer."},"contact_id":{"type":["string","null"],"format":"uuid","description":"The person at the customer it is addressed to; their email is where it is sent."},"sub_entity_id":{"type":["string","null"],"format":"uuid","description":"The office issuing it, for organizations with several."},"issue_date":{"type":["string","null"],"format":"date","description":"Provisional; replaced by the send date."},"due_date":{"type":["string","null"],"format":"date","description":"Provisional; replaced by send date + payment terms."},"delivery_date":{"type":["string","null"],"format":"date"},"payment_terms_days":{"type":"integer","minimum":0,"maximum":365},"currency":{"type":"string","minLength":3,"maxLength":3},"your_reference":{"type":["string","null"],"maxLength":128},"our_reference":{"type":["string","null"],"maxLength":128},"series":{"type":"string","maxLength":8,"default":"A","description":"Number series. Each series is numbered on its own."},"rot_rut":{"type":"string","enum":["none","rot","rut"],"default":"none","description":"ROT and RUT are included from the Väx tier of invoicing (`invoicing.tax_deduction`); setting `rot` or `rut` without it is answered `402` with that `feature`. `none` is always accepted."},"rot_rut_work_type":{"$ref":"#/components/schemas/RotRutWorkType","description":"Skatteverket's category for the work on every line that names none of its own (`rot_rut_work_type` on the line). The claim to Skatteverket declares the labour, hours and material per category."},"tax_profile_id":{"type":["string","null"],"format":"uuid","description":"Which of the customer's ROT/RUT tax profiles the deduction is claimed for. Must belong to the invoice's customer. Left out on a ROT/RUT invoice, the customer's only profile (or the one tied to the work order's service address) is used."},"delivery_method":{"type":"string","enum":["email","peppol","print"],"default":"email"},"notes":{"type":["string","null"]}},"additionalProperties":false},"InvoiceLine":{"type":"object","description":"A row of an invoice. Amounts in öre.","required":["id","sort_order","description","quantity","unit","unit_price_cents","discount_percent","vat_rate","net_cents","vat_cents","total_cents","line_type","rot_rut_eligible","rot_rut_work_type","is_generated","account_code","article_id","work_order_id","time_entry_id","metadata"],"properties":{"id":{"type":"string","format":"uuid"},"sort_order":{"type":"integer"},"description":{"type":"string"},"quantity":{"type":"number","description":"Negative on a credit note."},"unit":{"type":["string","null"],"description":"e.g. `tim`, `st`, `kvm`."},"unit_price_cents":{"type":"integer","description":"Excluding VAT."},"discount_percent":{"type":"number"},"vat_rate":{"type":"number","description":"Moms in percent: 25, 12, 6 or 0."},"net_cents":{"type":"integer"},"vat_cents":{"type":"integer"},"total_cents":{"type":"integer"},"line_type":{"type":"string","enum":["goods","service","labour","travel","fee","rot_rut_deduction"],"description":"Only `labour` and `service` can carry a ROT/RUT deduction. `rot_rut_deduction` is the generated deduction line."},"rot_rut_eligible":{"type":"boolean"},"rot_rut_work_type":{"$ref":"#/components/schemas/RotRutWorkType"},"is_generated":{"type":"boolean","description":"True for the ROT/RUT deduction line, which Closepact maintains and nobody can edit."},"account_code":{"type":["string","null"],"description":"BAS sales account, e.g. `3001` (25 %), `3002` (12 %), `3003` (6 %)."},"article_id":{"type":["string","null"],"format":"uuid","description":"The article it was copied from, for traceability."},"work_order_id":{"type":["string","null"],"format":"uuid"},"time_entry_id":{"type":["string","null"],"format":"uuid"},"metadata":{"type":["object","array"],"description":"On the deduction line: `eligible_gross_cents`, `rate_percent`, `customer_pays_cents`. An empty list otherwise.","additionalProperties":true}},"additionalProperties":false},"InvoiceLineFields":{"type":"object","description":"The writable fields of a line. `description` is required when adding a line without `article_id`; on update every field is optional.","properties":{"article_id":{"type":["string","null"],"format":"uuid","description":"Copy name, unit, price, VAT rate, account and eligibility from this article."},"work_order_id":{"type":["string","null"],"format":"uuid"},"time_entry_id":{"type":["string","null"],"format":"uuid"},"description":{"type":"string","maxLength":500},"quantity":{"type":"number","default":1},"unit":{"type":["string","null"],"maxLength":32},"unit_price_cents":{"type":"integer","default":0,"description":"Öre, excluding VAT."},"discount_percent":{"type":"number","minimum":0,"maximum":100,"default":0},"vat_rate":{"type":"number","enum":[25,12,6,0],"default":25},"line_type":{"type":"string","enum":["goods","service","labour","travel","fee"],"default":"goods"},"rot_rut_eligible":{"type":"boolean","default":false},"rot_rut_work_type":{"$ref":"#/components/schemas/RotRutWorkType","description":"Skatteverket's category for this line's work — or, on a goods line, for the work the material was for. Null falls back to the invoice's `rot_rut_work_type`."},"account_code":{"type":["string","null"],"maxLength":8},"sort_order":{"type":"integer","minimum":0}},"additionalProperties":false},"RotRutWorkType":{"type":["string","null"],"enum":["Bygg","El","GlasPlatarbete","MarkDraneringarbete","Murning","MalningTapetsering","Vvs","Stadning","KladOchTextilvard","Snoskottning","Tradgardsarbete","Barnpassning","Personligomsorg","Flyttjanster","ItTjanster","ReparationAvVitvaror","Moblering","TillsynAvBostad","TransportTillForsaljning","TvattVidTvattinrattning",null],"description":"Skatteverket's category for ROT or RUT work, as named in its claim file schema (version 6): `Bygg` to `Vvs` are ROT, `Stadning` to `TvattVidTvattinrattning` RUT. `TransportTillForsaljning` and `TvattVidTvattinrattning` are flat-rate services that are claimed without hours or material. Read when the claim to Skatteverket is filed; null when none is chosen."},"InvoiceRecord":{"type":"object","description":"An invoice exactly as stored, returned by send and credit. Dates are serialized as midnight UTC timestamps. Prefer `GET /invoices/{invoice}` for display.","required":["id","organization_id","sub_entity_id","invoice_number","series","type","credited_invoice_id","account_id","contact_id","status","issue_date","due_date","delivery_date","currency","your_reference","our_reference","payment_terms_days","net_cents","vat_cents","rounding_cents","total_cents","rot_rut","rot_rut_work_type","rot_rut_requested_cents","rot_rut_approved_cents","rot_rut_refused_cents","rot_rut_status","tax_profile_id","paid_cents","ocr_reference","delivery_method","sent_at","viewed_at","reminder_count","last_reminder_at","pdf_document_id","sie_exported_at","external_ref","notes","metadata","created_by","created_at","updated_at","deleted_at"],"properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"sub_entity_id":{"type":["string","null"],"format":"uuid"},"invoice_number":{"type":["string","null"]},"series":{"type":"string"},"type":{"type":"string","enum":["invoice","credit_note"]},"credited_invoice_id":{"type":["string","null"],"format":"uuid","description":"On a credit note, the invoice it credits."},"account_id":{"type":"string","format":"uuid"},"contact_id":{"type":["string","null"],"format":"uuid"},"status":{"$ref":"#/components/schemas/InvoiceStatus"},"issue_date":{"type":["string","null"],"format":"date-time"},"due_date":{"type":["string","null"],"format":"date-time"},"delivery_date":{"type":["string","null"],"format":"date-time"},"currency":{"type":"string"},"your_reference":{"type":["string","null"]},"our_reference":{"type":["string","null"]},"payment_terms_days":{"type":"integer"},"net_cents":{"type":"integer"},"vat_cents":{"type":"integer"},"rounding_cents":{"type":"integer"},"total_cents":{"type":"integer"},"rot_rut":{"type":"string","enum":["none","rot","rut"]},"rot_rut_work_type":{"$ref":"#/components/schemas/RotRutWorkType"},"rot_rut_requested_cents":{"type":"integer"},"rot_rut_approved_cents":{"type":"integer"},"rot_rut_refused_cents":{"type":"integer","description":"What Skatteverket refused of the deduction; owed by the customer again."},"rot_rut_status":{"type":"string"},"tax_profile_id":{"type":["string","null"],"format":"uuid","description":"The ROT/RUT tax profile the deduction is claimed for."},"paid_cents":{"type":"integer"},"ocr_reference":{"type":["string","null"]},"delivery_method":{"type":"string","enum":["email","peppol","print"]},"sent_at":{"type":["string","null"],"format":"date-time"},"viewed_at":{"type":["string","null"],"format":"date-time"},"reminder_count":{"type":"integer"},"last_reminder_at":{"type":["string","null"],"format":"date-time"},"pdf_document_id":{"type":["string","null"],"format":"uuid","description":"The stored PDF. Download it with `GET /invoices/{invoice}/pdf`."},"sie_exported_at":{"type":["string","null"],"format":"date-time","description":"When the invoice went to the books in an SIE export."},"external_ref":{"type":["object","array","null"],"additionalProperties":true},"notes":{"type":["string","null"]},"metadata":{"type":["object","array","null"],"description":"Working figures — `vat_by_rate`, `deduction`, `credits`, `source`, `delivery` — not a stable contract. `GET /invoices/{invoice}` carries `delivery` as a documented field.","additionalProperties":true},"created_by":{"type":["string","null"],"format":"uuid"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"deleted_at":{"type":["string","null"],"format":"date-time"},"recurring_invoice_id":{"type":["string","null"],"format":"uuid","description":"The recurring invoice that produced it."},"occurrence_date":{"type":["string","null"],"description":"For a recurring invoice, the period it bills."},"quote_id":{"type":["string","null"],"format":"uuid","description":"The accepted quote the invoice was drafted from, for an organization that invoices without scheduling."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceLineRecord"}},"account":{"$ref":"#/components/schemas/InvoiceCustomerRecord"},"contact":{"anyOf":[{"$ref":"#/components/schemas/CustomerContact"},{"type":"null"}]},"credited_invoice":{"description":"On a credit note from `POST /invoices/{invoice}/credit`: the original invoice, as stored.","anyOf":[{"$ref":"#/components/schemas/InvoiceRecord"},{"type":"null"}]}},"additionalProperties":false},"InvoiceLineRecord":{"type":"object","description":"An invoice line exactly as stored.","required":["id","customer_invoice_id","organization_id","sort_order","article_id","work_order_id","time_entry_id","description","quantity","unit","unit_price_cents","discount_percent","vat_rate","net_cents","vat_cents","total_cents","line_type","rot_rut_eligible","account_code","metadata","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"customer_invoice_id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"sort_order":{"type":"integer"},"article_id":{"type":["string","null"],"format":"uuid"},"work_order_id":{"type":["string","null"],"format":"uuid"},"time_entry_id":{"type":["string","null"],"format":"uuid"},"description":{"type":"string"},"quantity":{"type":"number"},"unit":{"type":["string","null"]},"unit_price_cents":{"type":"integer"},"discount_percent":{"type":"number"},"vat_rate":{"type":"number"},"net_cents":{"type":"integer"},"vat_cents":{"type":"integer"},"total_cents":{"type":"integer"},"line_type":{"type":"string","enum":["goods","service","labour","travel","fee","rot_rut_deduction"]},"rot_rut_eligible":{"type":"boolean"},"rot_rut_work_type":{"$ref":"#/components/schemas/RotRutWorkType"},"account_code":{"type":["string","null"]},"metadata":{"type":["object","array","null"],"additionalProperties":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"InvoiceCustomerRecord":{"type":"object","description":"The invoiced customer exactly as stored. The field names are the storage names: `invoice_delivery` and `einvoice_reference` are `invoicing.delivery` and `invoicing.reference` on a Customer.","required":["id","organization_id","name","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"domain":{"type":["string","null"]},"industry":{"type":["string","null"]},"size":{"type":["string","null"],"description":"Headcount band from a CRM."},"country":{"type":["string","null"]},"crm_external_id":{"type":["string","null"]},"crm_provider":{"type":["string","null"]},"metadata":{"type":["object","array","null"],"additionalProperties":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"deleted_at":{"type":["string","null"],"format":"date-time"},"sub_entity_id":{"type":["string","null"],"format":"uuid"},"customer_number":{"type":["string","null"]},"customer_type":{"type":"string","enum":["company","private"]},"org_number":{"type":["string","null"]},"vat_number":{"type":["string","null"]},"industry_code":{"type":["string","null"]},"payment_terms_days":{"type":"integer"},"late_interest_percent":{"type":["number","null"]},"vat_mode":{"type":"string","enum":["se","reverse","eu","export"]},"invoice_delivery":{"type":"string","enum":["email","peppol","print"]},"peppol_id":{"type":["string","null"]},"einvoice_reference":{"type":["string","null"]},"reminder_email":{"type":["string","null"]},"currency":{"type":"string"},"locale":{"type":"string"},"default_price_list_id":{"type":["string","null"],"format":"uuid"},"credit_limit_cents":{"type":["integer","null"]},"is_blocked":{"type":"boolean"},"notes":{"type":["string","null"]}},"additionalProperties":false},"InvoicePayment":{"type":"object","description":"Money received against an invoice. Amounts in öre.","required":["id","customer_invoice_id","paid_at","amount_cents","currency","method","from_customer","reference","bank_statement_line_id","note","created_by","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"customer_invoice_id":{"type":"string","format":"uuid"},"paid_at":{"type":["string","null"],"format":"date","description":"The day the money moved."},"amount_cents":{"type":"integer","description":"Negative for a refund."},"currency":{"type":"string"},"method":{"type":"string","enum":["bank","card","autogiro","cash","rot_rut","writeoff"],"description":"`rot_rut` is Skatteverket's settlement of the deduction and `writeoff` a decision to stop asking; neither is money from the customer."},"from_customer":{"type":"boolean","description":"False for `rot_rut` and `writeoff`."},"reference":{"type":["string","null"],"description":"The bank's reference, typically the OCR number."},"bank_statement_line_id":{"type":["string","null"],"format":"uuid","description":"Set when the payment came from a matched bank statement line."},"note":{"type":["string","null"]},"created_by":{"type":["string","null"],"description":"The **name** of the member who registered it (the key's member for API payments); null for automatic ones."},"created_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"InvoiceBalance":{"type":"object","description":"Where an invoice stands after its payments. Amounts in öre.","required":["id","reference","status","currency","total_cents","rot_rut_requested_cents","paid_cents","outstanding_cents","days_overdue"],"properties":{"id":{"type":"string","format":"uuid"},"reference":{"type":"string","description":"e.g. `A-1042`."},"status":{"$ref":"#/components/schemas/InvoiceStatus"},"currency":{"type":"string"},"total_cents":{"type":"integer"},"rot_rut_requested_cents":{"type":"integer","description":"Skatteverket's share, not owed by the customer."},"paid_cents":{"type":"integer"},"outstanding_cents":{"type":"integer","description":"Still owed by the customer."},"days_overdue":{"type":"integer"}},"additionalProperties":false},"InvoiceReminder":{"type":"object","description":"A payment reminder sent for an invoice, with the figures the customer was charged.","required":["id","customer_invoice_id","level","sent_at","fee_cents","interest_percent","interest_cents","channel","created_by","metadata","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"customer_invoice_id":{"type":"string","format":"uuid"},"level":{"type":"integer","minimum":1,"maximum":3,"description":"3 announces collection."},"sent_at":{"type":["string","null"],"format":"date-time"},"fee_cents":{"type":"integer","description":"Påminnelseavgift charged, in öre."},"interest_percent":{"type":"number","description":"Dröjsmålsränta per year used for the interest."},"interest_cents":{"type":"integer","description":"Interest charged up to the reminder, in öre."},"channel":{"type":"string","enum":["email","print"]},"created_by":{"type":["string","null"],"format":"uuid","description":"The member who sent it; null when the schedule did."},"metadata":{"type":["object","array"],"additionalProperties":true},"created_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"InvoiceSourceWarning":{"type":"object","description":"Something about a work order the person sending the invoice should know.","required":["code","message","context","work_order_id","order_number"],"properties":{"code":{"type":"string","description":"Known codes: `time_awaiting_approval`, `labour_rate_missing`, `fixed_price_missing`, `fixed_price_with_materials`, `not_completed`, `already_invoiced`."},"message":{"type":"string"},"context":{"type":["object","array"],"additionalProperties":true},"work_order_id":{"type":"string","format":"uuid"},"order_number":{"type":["string","null"]}},"additionalProperties":false},"ProductType":{"type":"string","description":"`one_time` for a single delivery, `recurring` for a subscription charged per `billing_cycle`, `service` for work, `bundle` for a package.","enum":["one_time","recurring","service","bundle"]},"Product":{"type":"object","required":["id","sku","name","description","type","unit_price_cents","vat_rate","currency","billing_cycle","is_active","service_id","metadata","properties","created_by_name","updated_by_name","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"sku":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"type":{"$ref":"#/components/schemas/ProductType"},"unit_price_cents":{"type":"integer","minimum":0,"description":"Price per unit in öre, excluding VAT: 160000 is 1 600 kr."},"vat_rate":{"type":"number","description":"Moms in percent: 25, 12, 6 or 0. Copied onto every quote line made from the product."},"currency":{"type":"string","minLength":3,"maxLength":3},"billing_cycle":{"type":["string","null"],"enum":["monthly","yearly",null]},"is_active":{"type":"boolean","description":"Whether the product is on sale."},"service_id":{"type":["string","null"],"format":"uuid","description":"The service in your scheduling catalogue this product is performed as, or null. When an accepted quote becomes a work order, a line made from this product becomes that service on the job; without a link it is carried over as a line of free text."},"metadata":{"type":["object","array"],"maxItems":0,"additionalProperties":true,"description":"Free-form values. An empty set comes back as `[]`."},"properties":{"type":["object","array"],"maxItems":0,"description":"The product's custom field values, keyed by property id (see [List product properties](/developers/reference/products#listProductProperties)). Numbers come back as JSON numbers, booleans as booleans and dates as `YYYY-MM-DD` strings. A product with no values has `[]`.","additionalProperties":{"type":["string","number","boolean"]}},"created_by_name":{"type":["string","null"],"description":"Name of the member who created the product."},"updated_by_name":{"type":["string","null"],"description":"Name of the member who last changed it."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"ProductEnvelope":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Product"}},"additionalProperties":false},"ProductPropertyValuesInput":{"type":["object","null"],"description":"Custom field values, keyed by property id or by its `key`. A value is converted to the property's type: numbers to decimals, booleans from true/false/1/0/\"yes\"/\"no\", dates to `YYYY-MM-DD`, everything else to text. `null` or an empty string clears the value. Properties not named are left as they are; unknown ids and keys are ignored.","additionalProperties":{"type":["string","number","boolean","null"]},"example":{"area_sqm":85,"rut":true}},"ProductProperty":{"type":"object","description":"A custom field every product in the organization can carry.","required":["id","organization_id","key","label","type","options","is_required","position","created_at","updated_at","deleted_at"],"properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"key":{"type":"string","description":"Stable machine name, usable instead of the id when writing values."},"label":{"type":"string"},"type":{"type":"string","enum":["text","number","boolean","date","select"]},"options":{"type":"array","items":{"type":"string"},"description":"The choices of a `select` property."},"is_required":{"type":"boolean"},"position":{"type":"integer"},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"},"deleted_at":{"type":["string","null"],"format":"date-time","description":"Always null here."}},"additionalProperties":false},"ProposalStatus":{"type":"string","description":"`draft` while being written; `in_review` when held for approval in the panel; `published` once released.","enum":["draft","in_review","published"]},"ProposalBlock":{"type":"object","description":"One node of the content tree, in the shape the editor (Tiptap) uses: block nodes hold `content`, `text` nodes hold `text` and optional `marks` (bold, italic, link…).","required":["type"],"properties":{"type":{"type":"string","enum":["paragraph","heading","bulletList","orderedList","listItem","image","video","fileAttachment","pricingTable","signature","divider","callout","aiBlock","codeBlock","blockquote","text","hardBreak"]},"attrs":{"type":["object","array"],"maxItems":0,"additionalProperties":true,"description":"Node settings, such as a heading's `level` or an image's `src`. An empty set comes back as `[]`."},"text":{"type":"string","maxLength":10000,"description":"For `text` nodes. Longer text is cut to 10 000 characters."},"content":{"type":"array","items":{"$ref":"#/components/schemas/ProposalBlock"}},"marks":{"type":"array","items":{"type":"object","additionalProperties":true}}},"additionalProperties":false},"Proposal":{"type":"object","required":["id","title","status","current_version","deal_id","deal_room_id","blocks","settings","published_at","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"status":{"$ref":"#/components/schemas/ProposalStatus"},"current_version":{"type":"integer","minimum":1,"description":"Goes up by one with every content change."},"deal_id":{"type":["string","null"],"format":"uuid"},"deal_room_id":{"type":["string","null"],"format":"uuid"},"blocks":{"type":"array","items":{"$ref":"#/components/schemas/ProposalBlock"}},"settings":{"type":["object","array","null"],"maxItems":0,"additionalProperties":true,"description":"Editor display settings. An empty set comes back as `[]`."},"published_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"ProposalEnvelope":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Proposal"}},"additionalProperties":false},"QuoteStatus":{"type":"string","description":"`draft` until sent, and the only state whose lines and terms can change; `sent` once in front of the buyer, who can accept or reject it until the end of its `expires_at` day; `accepted` or `rejected` by the buyer, after which it never changes; `expired` when a sent quote's last day passed unanswered (set by an hourly sweep). A sent or expired quote goes back to `draft` only through [Revert to draft](/developers/reference/quotes#revertQuoteToDraft).","enum":["draft","sent","accepted","rejected","expired"]},"Quote":{"type":"object","required":["id","deal_id","deal_room_id","quote_number","title","status","subtotal_cents","discount_cents","discount_percent","tax_cents","tax_percent","total_cents","vat_breakdown","rot_rut","currency","expires_at","accepted_at","accepted_by_stakeholder_id","acceptance","rejection_reason","share_url","handover","lines","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"deal_id":{"type":["string","null"],"format":"uuid"},"deal_room_id":{"type":["string","null"],"format":"uuid","description":"The room whose stakeholders can accept the quote."},"quote_number":{"type":"string","description":"Sequential within the organization.","example":"Q-00042"},"title":{"type":"string"},"status":{"$ref":"#/components/schemas/QuoteStatus"},"subtotal_cents":{"type":"integer","description":"Sum of the totals of the lines that count, in öre, before the quote discount and VAT."},"discount_cents":{"type":"integer","description":"What the quote's `discount_percent` took off those lines, in öre."},"discount_percent":{"type":"integer","minimum":0,"maximum":100},"tax_cents":{"type":"integer","description":"The VAT of the lines that count, summed — each line's rounded once. Per rate in `vat_breakdown`."},"tax_percent":{"type":"integer","minimum":0,"maximum":100,"deprecated":true,"description":"Deprecated: VAT is per line (`lines[].vat_rate`, `vat_breakdown`). Kept for older integrations: it reads as the rate every counted line shares, and is left as it was when the rates are mixed. It never changes an amount."},"total_cents":{"type":"integer","description":"What the quote comes to, VAT included, in öre: 400000 is 4 000 kr. Before any ROT/RUT deduction — see `rot_rut.customer_pays_cents`."},"vat_breakdown":{"type":"array","description":"Net and VAT per rate over the lines that count, highest rate first — as an invoice has to state them.","items":{"type":"object","required":["rate","base_cents","vat_cents"],"properties":{"rate":{"type":"number","description":"25, 12, 6 or 0."},"base_cents":{"type":"integer","description":"Net of the lines at this rate, after discounts."},"vat_cents":{"type":"integer"}},"additionalProperties":false}},"rot_rut":{"type":"object","description":"A preliminary ROT/RUT estimate, kept apart from the total. The yearly ceiling per person is not applied — it depends on what other companies have already claimed — and Skatteverket decides after the invoice.","required":["type","rate_percent","labour_cents","deduction_cents","customer_pays_cents","preliminary","annual_ceiling_applied"],"properties":{"type":{"type":"string","enum":["none","rot","rut"]},"rate_percent":{"type":["integer","null"],"enum":[30,50,null],"description":"30 for ROT, 50 for RUT, null for none."},"labour_cents":{"type":"integer","description":"The counted `labour` lines including VAT — what the deduction is a share of. Materials never count."},"deduction_cents":{"type":"integer","description":"The estimated deduction, claimed from Skatteverket. 0 when `type` is `none`."},"customer_pays_cents":{"type":"integer","description":"`total_cents` − `deduction_cents`: what the buyer pays after the deduction."},"preliminary":{"type":"boolean","enum":[true],"description":"Always true: an estimate, not a decision."},"annual_ceiling_applied":{"type":"boolean","enum":[false],"description":"Always false."}},"additionalProperties":false},"currency":{"type":"string","minLength":3,"maxLength":3},"expires_at":{"type":["string","null"],"format":"date","description":"Last day the quote can be accepted (inclusive)."},"accepted_at":{"type":["string","null"],"format":"date-time"},"accepted_by_stakeholder_id":{"type":["string","null"],"format":"uuid","description":"The room stakeholder who accepted it, when it was accepted in a room."},"acceptance":{"type":["object","null"],"description":"What the buyer agreed to, recorded once at the moment of acceptance. Null until accepted, and on quotes accepted before this was recorded.","required":["via","name","email","selected_optional_line_ids","total_cents","tax_cents","customer_pays_cents"],"properties":{"via":{"type":["string","null"],"enum":["room","link",null],"description":"`room` — by a stakeholder in a deal room; `link` — through the quote's own link."},"name":{"type":["string","null"],"description":"Who accepted."},"email":{"type":["string","null"]},"selected_optional_line_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The optional lines the buyer ticked."},"total_cents":{"type":"integer","description":"The quote's total at acceptance, VAT included."},"tax_cents":{"type":"integer"},"customer_pays_cents":{"type":"integer","description":"The total less the preliminary ROT/RUT deduction."}},"additionalProperties":false},"rejection_reason":{"type":["string","null"],"description":"What the buyer wrote when rejecting it."},"share_url":{"type":["string","null"],"format":"uri","description":"The quote's own page, where the buyer can read it, tick options and accept or reject it without a deal room. Issued when the quote is sent, revoked when it is reverted to draft (a new one comes with the next send). Null on a draft. Anyone holding it can answer the quote, so share it only with the buyer."},"handover":{"$ref":"#/components/schemas/QuoteHandover"},"lines":{"type":"array","items":{"$ref":"#/components/schemas/QuoteLine"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"QuoteHandover":{"type":"object","description":"What an accepted quote becomes in your organization, and what it became. With the scheduling module it becomes a work order waiting to be planned; with invoicing and no scheduling, an invoice draft. `available`, `target` and `mode` describe your organization and are the same on every quote; `work_order`, `invoice` and `blocked_reason` describe this one. See [Hand over an accepted quote](/developers/reference/quotes#convertQuote).","required":["available","target","mode","work_order","invoice","blocked_reason"],"properties":{"available":{"type":"boolean","description":"Whether accepted quotes can be handed over here: the organization has the capability (`proposals.convert`, proposals Väx and Pro) and a module to hand them to."},"target":{"type":["string","null"],"enum":["work_order","invoice",null],"description":"What an accepted quote becomes: `work_order` with the scheduling module, otherwise `invoice` with the invoicing module, otherwise null."},"mode":{"type":"string","enum":["automatic","manual"],"description":"`automatic`: the work order or draft is made the moment the buyer accepts (the default). `manual`: it waits for [Hand over an accepted quote](/developers/reference/quotes#convertQuote) — the *Create job* button in the panel."},"work_order":{"type":["object","null"],"description":"The work order this quote became, or null. A work order that was deleted is not shown.","required":["id","order_number","status"],"properties":{"id":{"type":"string","format":"uuid"},"order_number":{"type":"string"},"status":{"type":"string","enum":["booked","scheduled","in_progress","completed","invoiced","cancelled"]}},"additionalProperties":false},"invoice":{"type":["object","null"],"description":"The invoice draft this quote became, or null. Only one of `work_order` and `invoice` is ever set.","required":["id","status","reference"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["draft","sent","partially_paid","paid","overdue","credited","cancelled","written_off"]},"reference":{"type":"string","description":"The invoice number once it is sent; `Utkast` while it is a draft."}},"additionalProperties":false},"blocked_reason":{"type":["string","null"],"enum":["missing_customer",null],"description":"Why acceptance could not hand the quote over by itself: `missing_customer` — the quote is tied to no customer (no deal names one, and the buyer is not a contact of one). Hand it over with an `account_id`. Null once it has been handed over."}},"additionalProperties":false},"QuoteHandoverOrigin":{"type":"object","description":"The accepted quote a work order or an invoice draft was made from, and what the handover could not fill in.","required":["quote_id","quote_number","created_automatically","warnings"],"properties":{"quote_id":{"type":"string","format":"uuid","description":"The accepted quote."},"quote_number":{"type":["string","null"],"description":"Its number, as it was when the handover was made."},"created_automatically":{"type":"boolean","description":"True when the buyer's acceptance made it; false when a person or an API call did."},"warnings":{"type":"array","description":"What could not be filled in, for a person to settle: `service_address_missing` and `service_address_ambiguous` (a work order with no service address: the customer has none, or several and no default), `deduction_not_included` (the quote estimated ROT/RUT and the invoicing tier cannot claim it, so the draft has none), `tax_profile_missing` (the buyer of a ROT/RUT draft could not be told from the customer), `recurring_lines` (lines charged monthly or yearly were carried over once), `no_labour_lines` (the quote had only materials, so hours reported on the work order are billed at each person's rate) and `no_lines`. New codes may be added.","items":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string","description":"The same, for a person, in English."}},"additionalProperties":false}}},"additionalProperties":false},"QuoteEnvelope":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Quote"}},"additionalProperties":false},"QuoteLine":{"type":"object","required":["id","product_id","name","description","quantity","unit_price_cents","discount_percent","vat_rate","line_total_cents","net_cents","vat_cents","line_type","is_optional","is_selected","billing_cycle","position"],"properties":{"id":{"type":"string","format":"uuid"},"product_id":{"type":["string","null"],"format":"uuid","description":"The catalogue product the line was made from, if any."},"name":{"type":"string"},"description":{"type":["string","null"]},"quantity":{"type":"number","minimum":0.001,"description":"Up to three decimals. A whole quantity is sent as an integer (`2`), a fractional one as a number (`2.5`)."},"unit_price_cents":{"type":"integer","minimum":0,"description":"In öre, excluding VAT."},"discount_percent":{"type":"integer","minimum":0,"maximum":100},"vat_rate":{"type":"number","description":"Moms in percent: 25, 12, 6 or 0."},"line_total_cents":{"type":"integer","description":"`quantity × unit_price_cents` less the line discount, in öre, rounded half up once."},"net_cents":{"type":"integer","description":"The line total less its share of the quote discount: what VAT is charged on."},"vat_cents":{"type":"integer","description":"`vat_rate` of `net_cents`, rounded half up once."},"line_type":{"type":"string","enum":["labour","material","other"],"description":"Only `labour` counts towards a ROT/RUT deduction."},"is_optional":{"type":"boolean","description":"An option the buyer may tick in or out."},"is_selected":{"type":"boolean","description":"Whether the line counts in the totals. Always true for a fixed line; for an optional one, the seller's suggestion until the buyer accepts, and the buyer's choice after."},"billing_cycle":{"type":["string","null"],"enum":["monthly","yearly",null]},"position":{"type":"integer","minimum":0,"description":"Order on the quote, from 0."}},"additionalProperties":false},"RecurrenceTemplate":{"type":"object","description":"What each generated job looks like. Only these keys exist; any other key is refused with 422 (a misspelt `titel` would otherwise be dropped silently).","properties":{"title":{"type":"string","description":"Title of each job.","maxLength":255},"description":{"type":["string","null"],"description":"Description of each job."},"contact_id":{"type":["string","null"],"format":"uuid","description":"Contact at the customer."},"service_address_id":{"type":["string","null"],"format":"uuid","description":"Where the work is done: one of the customer's addresses."},"priority":{"type":"integer","description":"-2 to 2.","minimum":-2,"maximum":2},"price_model":{"type":"string","enum":["hourly","fixed","per_unit"],"description":"How each job is charged."},"agreed_price_cents":{"type":["integer","null"],"description":"Price per visit for a `fixed` job, in öre excluding VAT.","minimum":0},"rot_rut":{"type":"string","enum":["none","rot","rut"],"description":"ROT/RUT deduction on each job."},"rot_rut_share":{"type":["number","null"],"description":"Share of the deduction, 0–100 percent.","minimum":0,"maximum":100},"estimated_minutes":{"type":["integer","null"],"description":"Expected duration in minutes.","minimum":1},"start_time":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Time of day each job starts, `HH:MM`, on the organization's local clock (Swedish time, `Europe/Stockholm`): `06:30` is 06:30 in Stockholm all year, so the job's `planned_start` is 05:30 UTC in winter and 04:30 UTC in summer. Converted per occurrence, so it stays put across daylight-saving changes. Default `08:00`."},"duration_minutes":{"type":"integer","description":"Planned length of each visit, 1–1440 minutes; sets `planned_end`. Without it (or `estimated_minutes`) the jobs have no planned end.","minimum":1,"maximum":1440}},"additionalProperties":false},"Recurrence":{"type":"object","description":"A standing agreement (återkommande avtal) — \"every other Monday, stairwell cleaning\" — and the rule that writes its jobs.","required":["id","sub_entity_id","account_id","customer","rrule","template","starts_on","ends_on","next_run_on","status","lead_time_days","last_generated_at","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The recurrence id."},"sub_entity_id":{"type":["string","null"],"format":"uuid","description":"The office (sub-entity) it belongs to, and every job it writes; or null."},"account_id":{"type":"string","format":"uuid","description":"The customer."},"customer":{"description":"The customer, or null where not loaded (the `recurrence` inside a generate result).","oneOf":[{"$ref":"#/components/schemas/WorkOrderCustomer"},{"type":"null"}]},"rrule":{"type":"string","description":"iCalendar recurrence rule (RFC 5545), limited to `FREQ=DAILY|WEEKLY|MONTHLY`, `INTERVAL`, `BYDAY` (`MO`…`SU`, no positions), `COUNT` and `UNTIL`. Weeks start on Monday."},"template":{"$ref":"#/components/schemas/RecurrenceTemplate"},"starts_on":{"type":"string","format":"date","description":"First day of the agreement. Calendar date, `YYYY-MM-DD`."},"ends_on":{"type":["string","null"],"format":"date","description":"Last day, or null for open-ended. Calendar date, `YYYY-MM-DD`."},"next_run_on":{"type":["string","null"],"format":"date","description":"The next date the rule will write a job for, or null when it has run out. Calendar date, `YYYY-MM-DD`."},"status":{"type":"string","enum":["active","paused","completed"],"description":"`active` agreements write jobs; `paused` ones wait (a customer away for the summer); `completed` ones have run out — set by the generator when the rule produces no more dates, or by you."},"lead_time_days":{"type":"integer","description":"How far ahead jobs are written, in days (1–120)."},"last_generated_at":{"type":["string","null"],"format":"date-time","description":"When jobs were last generated, or null."},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"updated_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."}},"additionalProperties":false},"RecurrenceRun":{"type":"object","description":"The result of running the generator for one agreement.","required":["created","skipped","horizon","dates","work_orders","recurrence"],"properties":{"created":{"type":"integer","description":"Jobs written by this run. 0 for a dry run."},"skipped":{"type":"integer","description":"Dates that already had a job (written by an earlier run, or deleted on purpose)."},"horizon":{"type":"string","format":"date","description":"The last date this run looked at: today plus `lead_time_days`, or `ends_on` if earlier. Calendar date, `YYYY-MM-DD`."},"dates":{"type":"array","description":"Every date from today to the horizon the rule produces (at most 200), whether written now or before.","items":{"type":"string","format":"date"}},"work_orders":{"type":"array","description":"The jobs written by this run.","items":{"type":"object","required":["id","order_number","title","occurrence_date","planned_start"],"properties":{"id":{"type":"string","format":"uuid","description":"The work order id."},"order_number":{"type":"string","description":"Its number."},"title":{"type":"string","description":"Its title."},"occurrence_date":{"type":"string","format":"date","description":"The date it was written for. Calendar date, `YYYY-MM-DD`."},"planned_start":{"type":["string","null"],"format":"date-time","description":"Its planned start."}},"additionalProperties":false}},"recurrence":{"$ref":"#/components/schemas/Recurrence"}},"additionalProperties":false},"RoomStatus":{"type":"string","description":"`draft` until published; `published` while buyers can open it; `expired` once closed to buyers; `archived` when put away in the panel.","enum":["draft","published","expired","archived"]},"Room":{"type":"object","required":["id","slug","title","description","status","published_at","expires_at","require_email","password_protected","brand_overrides","settings","deal_id","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string","description":"The unguessable part of the room's public link."},"title":{"type":"string"},"description":{"type":["string","null"]},"status":{"$ref":"#/components/schemas/RoomStatus"},"published_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time","description":"When buyers lose access; null for never."},"require_email":{"type":"boolean","description":"Whether visitors must give their e-mail before the room opens."},"password_protected":{"type":"boolean","description":"Whether a password is required. The password itself is never returned."},"brand_overrides":{"type":["object","array","null"],"maxItems":0,"additionalProperties":true,"description":"An empty set comes back as `[]`."},"settings":{"type":["object","array","null"],"maxItems":0,"additionalProperties":true,"description":"An empty set comes back as `[]`."},"deal_id":{"type":["string","null"],"format":"uuid"},"created_by":{"type":"object","description":"The member who created the room. Left out of the answers to publish (when already published) and expire.","required":["id","name","email"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string","format":"email"}},"additionalProperties":false},"contents":{"type":"array","description":"The room's content blocks in display order. Only on [Retrieve a room](/developers/reference/rooms#getRoom).","items":{"$ref":"#/components/schemas/RoomContent"}},"stakeholders":{"type":"array","description":"The room's stakeholders. Only on [Retrieve a room](/developers/reference/rooms#getRoom).","items":{"$ref":"#/components/schemas/Stakeholder"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"RoomEnvelope":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Room"}},"additionalProperties":false},"RoomContentSection":{"type":"string","description":"The part of the room page a block appears in.","enum":["overview","documents","pricing","map","attachments"]},"RoomContentKind":{"type":"string","description":"What the block is.","enum":["section","text","video","link","document","proposal","quote"]},"RoomContent":{"type":"object","required":["id","section","kind","title","body","url","payload","position","content_type","content_id"],"properties":{"id":{"type":"string","format":"uuid"},"section":{"$ref":"#/components/schemas/RoomContentSection"},"kind":{"$ref":"#/components/schemas/RoomContentKind"},"title":{"type":["string","null"]},"body":{"type":["string","null"]},"url":{"type":["string","null"]},"payload":{"type":["object","array","null"],"maxItems":0,"additionalProperties":true,"description":"An empty set comes back as `[]`."},"position":{"type":"integer","description":"Order within the section, starting at 1."},"content_type":{"type":["string","null"]},"content_id":{"type":["string","null"],"format":"uuid"}},"additionalProperties":false},"StakeholderRole":{"type":"string","description":"The part the person plays in the buying decision. Defaults to `user`.","enum":["champion","economic","technical","user","legal","procurement","other"],"default":"user"},"Stakeholder":{"type":"object","required":["id","email","name","role","status","engagement_score","influence_score","last_active_at","invited_at"],"properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email","description":"Lower case."},"name":{"type":["string","null"]},"role":{"$ref":"#/components/schemas/StakeholderRole"},"status":{"type":"string","enum":["invited","active"],"description":"`invited` until they first engage with the room, then `active`."},"engagement_score":{"type":"integer","minimum":0,"maximum":100,"description":"Grows as the person views and interacts with the room."},"influence_score":{"type":"integer"},"last_active_at":{"type":["string","null"],"format":"date-time"},"invited_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"Service":{"type":"object","description":"Something the company sells by the visit (tjänst): what the schedule places, the booking widget offers and the invoice prices.","required":["id","name","description","default_duration_min","pricing","color","is_active","sub_entity_id","skills","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The service id."},"name":{"type":"string","description":"Name, e.g. \"Flyttstädning\"."},"description":{"type":["string","null"],"description":"Description, or null."},"default_duration_min":{"type":"integer","description":"How long a visit usually takes, in minutes; what the calendar places a job as."},"pricing":{"type":"object","required":["model","unit_price_cents","unit_label","vat_rate","rot_rut_eligible"],"properties":{"model":{"type":"string","enum":["hourly","fixed","per_unit"],"description":"Priced per hour, per visit, or per unit."},"unit_price_cents":{"type":"integer","description":"Amount in öre (1 SEK = 100 öre), excluding VAT. Per hour, visit or unit."},"unit_label":{"type":["string","null"],"description":"What a unit is called, e.g. \"timme\", \"fönster\", or null."},"vat_rate":{"type":"number","description":"VAT in percent: 25, 12, 6 or 0."},"rot_rut_eligible":{"type":"boolean","description":"Whether the work qualifies for ROT/RUT."}},"additionalProperties":false},"color":{"type":["string","null"],"description":"Colour on the board, or null."},"is_active":{"type":"boolean","description":"Whether it can be chosen for new work. Retire a service by setting this to false."},"sub_entity_id":{"type":["string","null"],"format":"uuid","description":"The office (sub-entity) the service belongs to, or null for all."},"skills":{"type":"array","description":"Competences required to do it. Empty means anybody can.","items":{"type":"object","required":["id","name","color"],"properties":{"id":{"type":"string","format":"uuid","description":"The skill id (see `GET /skills`)."},"name":{"type":"string","description":"Skill name."},"color":{"type":["string","null"],"description":"Colour, or null."}},"additionalProperties":false}},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"updated_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."}},"additionalProperties":false},"SignatureRequest":{"type":"object","description":"One round of sending a document for signing.","required":["id","title","message","status","signing_order","sent_at","completed_at","expires_at","canceled_at","cancel_reason","document","signers","deal_room_id","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"message":{"type":["string","null"]},"status":{"type":"string","enum":["draft","sent","partially_signed","completed","declined","expired","canceled"],"description":"`sent` → `partially_signed` → `completed`; or `declined`, `expired`, `canceled`. `draft` is not used by the API."},"signing_order":{"type":"string","enum":["parallel","sequential"]},"sent_at":{"type":["string","null"],"format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time","description":"When the last signer signed."},"expires_at":{"type":["string","null"],"format":"date-time"},"canceled_at":{"type":["string","null"],"format":"date-time"},"cancel_reason":{"type":["string","null"]},"document":{"type":["object","null"],"description":"The document being signed; null once it has been deleted.","required":["id","title","mime_type","size_bytes"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"mime_type":{"type":["string","null"]},"size_bytes":{"type":"integer"}},"additionalProperties":false},"signers":{"type":"array","description":"In signing order.","items":{"$ref":"#/components/schemas/SignatureSigner"}},"events":{"type":"array","description":"The audit trail, oldest first. Only on `GET /signatures/{signature}` and the cancel response; `chain` beside it says whether it verifies.","items":{"$ref":"#/components/schemas/SignatureEvent"}},"chain":{"$ref":"#/components/schemas/SignatureChainReport"},"deal_room_id":{"type":["string","null"],"format":"uuid"},"created_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"SignatureSigner":{"type":"object","description":"A person on a signature request. On requests made by `POST /documents/{document}/send` this includes `cc` and `viewer` recipients; their roles are on the document's `recipients`.","required":["id","email","name","order_index","status","opened_at","signed_at","declined_at","decline_reason","signature_type"],"properties":{"id":{"type":"string","format":"uuid","description":"Same id as the document recipient."},"email":{"type":"string","format":"email"},"name":{"type":"string"},"order_index":{"type":"integer"},"status":{"type":"string","enum":["pending","opened","signed","declined"]},"opened_at":{"type":["string","null"],"format":"date-time"},"signed_at":{"type":["string","null"],"format":"date-time"},"declined_at":{"type":["string","null"],"format":"date-time"},"decline_reason":{"type":["string","null"]},"signature_type":{"type":["string","null"],"enum":["typed","drawn","bankid",null],"description":"How they signed: a typed name, a drawn signature, or Swedish BankID (the BankID signature and its OCSP response are kept as evidence; their hashes are in the `signer.signed` event). Null until signed."}},"additionalProperties":false},"SignatureEvent":{"type":"object","description":"One entry in a signature request's hash-chained audit trail.","required":["id","signer_id","sequence","event_type","payload","hash_prev","hash_self","hash_version","ip_address","user_agent","occurred_at"],"properties":{"id":{"type":"string","format":"uuid"},"signer_id":{"type":["string","null"],"format":"uuid","description":"The signer it concerns, if any."},"sequence":{"type":["integer","null"],"description":"Position in the chain, from 1 and without gaps. Null on legacy events (`hash_version` 1)."},"event_type":{"type":"string","enum":["signature.requested","recipient.added","recipient.removed","signer.opened","signer.code_sent","signer.verified","signer.signed","signer.declined","signature.refused","reminder.sent","signature.completed","signature.sealed","signature.canceled"]},"payload":{"type":["object","array","null"],"description":"Details of the event. Sent as an empty array `[]` when there are none.","additionalProperties":true},"hash_prev":{"type":["string","null"],"description":"`hash_self` of the previous event; null on the first."},"hash_self":{"type":"string","description":"SHA-256 (hex) over this event and `hash_prev`."},"hash_version":{"type":"integer","enum":[1,2],"description":"Which fields `hash_self` covers; see `GET /signatures/{signature}`."},"ip_address":{"type":["string","null"]},"user_agent":{"type":["string","null"]},"occurred_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"SignatureChainReport":{"type":"object","description":"The audit chain of one signature request, recomputed by the server.","required":["signature_request_id","status","format","event_count","head","broken_at"],"properties":{"signature_request_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["ok","broken","empty"],"description":"`ok`: every hash and link verifies. `broken`: see `broken_at`. `empty`: no events."},"format":{"type":["string","null"],"enum":["current","legacy","mixed",null],"description":"`current`: every event is sequenced (`hash_version` 2). `legacy`: only events from before October 2026, whose hashes are checked but whose order rests on a one-second timestamp. `mixed`: legacy events followed by current ones."},"event_count":{"type":"integer"},"head":{"type":["string","null"],"description":"`hash_self` of the newest event."},"broken_at":{"type":["object","null"],"required":["event_id","position","reason"],"properties":{"event_id":{"type":["string","null"],"format":"uuid","description":"The first event that does not verify; null for `truncated`."},"position":{"type":["integer","null"],"description":"Its position in the chain, from 1."},"reason":{"type":"string","enum":["hash_mismatch","prev_mismatch","sequence_gap","truncated","signer_detached"],"description":"`hash_mismatch`: the event was altered. `prev_mismatch`: it does not follow the event before it (one was removed or inserted). `sequence_gap`: a sequence number is missing or repeated. `truncated`: the newest events are gone. `signer_detached`: a legacy event lost its signer id when a recipient was deleted (before October 2026), so its hash cannot be recomputed."}},"additionalProperties":false}},"additionalProperties":false},"SignatureSignerLink":{"type":"object","description":"A signer's private signing link. Anyone holding it can sign as that person.","required":["signer_id","email","name","token","url"],"properties":{"signer_id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"name":{"type":"string"},"token":{"type":"string","description":"The secret in the link."},"url":{"type":"string","format":"uri"}},"additionalProperties":false},"Template":{"type":"object","description":"A reusable starting point.","required":["id","name","type","scope","is_built_in","description","is_active","usage_count","last_used_at","created_at","preview"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"is_built_in":{"type":"boolean","description":"True for a Closepact library template shared by every organization. It can be used but not deleted."},"type":{"type":"string","enum":["room","document","proposal","map","email"],"description":"What it makes: a deal room, a document, a proposal, a mutual action plan, or an email."},"scope":{"type":"string","enum":["personal","team","organization"]},"description":{"type":["string","null"]},"is_active":{"type":"boolean"},"usage_count":{"type":["integer","null"],"description":"Times it has been used. Null in the response that creates it."},"last_used_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"preview":{"type":"object","required":["sections","content_count","has_branding"],"properties":{"sections":{"type":["object","array"],"description":"Section → number of content rows in it. Sent as an empty array `[]` when the template has none.","additionalProperties":{"type":"integer"}},"content_count":{"type":"integer"},"has_branding":{"type":"boolean","description":"Whether it overrides the organization's brand kit."}},"additionalProperties":false},"content":{"type":["object","array","null"],"description":"The template's body; its shape depends on `type`. Only on `GET /templates/{template}`, or with `include_content=1` on the list. Sent as an empty array `[]` when empty.","additionalProperties":true}},"additionalProperties":false},"TimeEntry":{"type":"object","description":"One stretch of reported time. What the worker reported (`started_at`, `ended_at`, `break_minutes`), what their phone said (coordinates, accuracy, `*_source`) and what was concluded (`within_geofence`, `status`) are kept apart.","required":["id","organization_id","employee_id","work_order_id","started_at","clock_in_received_at","ended_at","clock_out_received_at","break_minutes","duration_minutes","type","start_lat","start_lng","start_accuracy_m","start_source","end_lat","end_lng","end_accuracy_m","end_source","within_geofence","status","approved_by","approved_at","rejection_reason","billable","hourly_rate_cents","billing_rate_cents","note","metadata","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The time entry id."},"organization_id":{"type":"string","format":"uuid","description":"Your organization."},"employee_id":{"type":"string","format":"uuid","description":"Whose time it is."},"work_order_id":{"type":["string","null"],"format":"uuid","description":"The job it was spent on, or null (workshop, travel between jobs)."},"started_at":{"type":"string","format":"date-time","description":"Clock-in, by the phone’s clock at the moment of the tap. A clock-in queued without signal keeps the time it was made, not the time it reached the server."},"clock_in_received_at":{"type":["string","null"],"format":"date-time","description":"When the server received the clock-in. Far later than `started_at` means the phone was offline; null for entries typed in by the office."},"ended_at":{"type":["string","null"],"format":"date-time","description":"Clock-out, or null while `open`."},"clock_out_received_at":{"type":["string","null"],"format":"date-time","description":"When the server received the clock-out, or null."},"break_minutes":{"type":"integer","description":"Unpaid break within the stretch, in minutes."},"duration_minutes":{"type":["integer","null"],"description":"Paid minutes: from start to end less the break. Written when the entry is closed or edited, never recomputed later. Null while `open`."},"type":{"type":"string","enum":["work","travel","overtime"],"description":"Kind of time."},"start_lat":{"type":["number","null"],"description":"Latitude at clock-in, or null."},"start_lng":{"type":["number","null"],"description":"Longitude at clock-in, or null."},"start_accuracy_m":{"type":["integer","null"],"description":"Reported accuracy of that position in metres, or null."},"start_source":{"type":"string","enum":["gps","manual","office"],"description":"Where the start came from: the phone's position, the phone without a position, or typed in afterwards by the office."},"end_lat":{"type":["number","null"],"description":"Latitude at clock-out, or null."},"end_lng":{"type":["number","null"],"description":"Longitude at clock-out, or null."},"end_accuracy_m":{"type":["integer","null"],"description":"Reported accuracy in metres, or null."},"end_source":{"type":"string","enum":["gps","manual","office"],"description":"Where the end came from."},"within_geofence":{"type":["boolean","null"],"description":"Whether the clock-in was inside the job address's geofence; null when there was nothing to compare (no position, or no fence on the address)."},"status":{"type":"string","enum":["open","submitted","approved","rejected"],"description":"`open`: clocked in, still running. `submitted`: clocked out, waiting for approval. `approved`: signed off — billable, and ready for payroll. `rejected`: sent back, with a `rejection_reason`."},"approved_by":{"type":["string","null"],"format":"uuid","description":"The member who approved — or rejected — it, or null."},"approved_at":{"type":["string","null"],"format":"date-time","description":"When it was approved, or null (also after a rejection)."},"rejection_reason":{"type":["string","null"],"description":"Why it was rejected, or null."},"billable":{"type":"boolean","description":"Whether the time may be invoiced. Unbillable time is still paid and still shown on the invoice basis, as an unbilled line. `travel` reported from the field app starts unbillable until somebody marks it billable; billable travel is invoiced as travel, never with a ROT/RUT deduction."},"hourly_rate_cents":{"type":["integer","null"],"description":"The employee's `hourly_cost_cents` copied at approval, in öre; null before. What the hour cost — for payroll and margin, never invoiced. A later change of the employee's rate does not change it."},"billing_rate_cents":{"type":["integer","null"],"description":"The employee's `hourly_billing_rate_cents` copied at approval, in öre excluding VAT; null before. What the hour is invoiced at when the job was not quoted one service price per hour. A later change of the employee's rate does not change it."},"note":{"type":["string","null"],"description":"Free text, or null."},"metadata":{"type":"object","description":"Free-form data.","additionalProperties":true},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"updated_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."}},"additionalProperties":false},"TimeEntryListItem":{"description":"A time entry with the employee and job it belongs to.","allOf":[{"$ref":"#/components/schemas/TimeEntry"},{"type":"object","required":["employee","work_order"],"properties":{"employee":{"description":"The employee, or null if they have been deleted.","oneOf":[{"type":"object","required":["id","first_name","last_name"],"properties":{"id":{"type":"string","format":"uuid","description":"The employee id."},"first_name":{"type":"string","description":"First name."},"last_name":{"type":"string","description":"Last name."}},"additionalProperties":false},{"type":"null"}]},"work_order":{"description":"The job, or null.","oneOf":[{"type":"object","required":["id","order_number","title"],"properties":{"id":{"type":"string","format":"uuid","description":"The work order id."},"order_number":{"type":"string","description":"Its number."},"title":{"type":"string","description":"Its title."}},"additionalProperties":false},{"type":"null"}]}},"additionalProperties":false}]},"WebhookEventName":{"type":"string","description":"An event an endpoint can subscribe to, or `*` for every event (including ones added later).","enum":["deal.created","deal.stage_changed","deal.won","deal.lost","room.created","room.published","stakeholder.added","message.posted","quote.accepted","signature.requested","document.viewed","signer.signed","signature.completed","signature.canceled","work_order.completed","invoice.issued","work_order.created_from_quote","invoice.created_from_quote","*"]},"WebhookEndpoint":{"type":"object","description":"A URL that receives webhook events. The signing secret is never part of it, except in the responses to create and rotate-secret.","required":["id","url","description","events","status","last_success_at","last_failure_at","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri","description":"Where events are POSTed."},"description":{"type":["string","null"],"description":"Your note on what the endpoint is for."},"events":{"type":"array","description":"The events it receives (see WebhookEventName); `[\"*\"]` for all.","items":{"type":"string"}},"status":{"type":"string","enum":["active","paused"],"description":"`paused` endpoints receive no new events."},"last_success_at":{"type":["string","null"],"format":"date-time","description":"When a delivery to it last succeeded."},"last_failure_at":{"type":["string","null"],"format":"date-time","description":"When an attempt to deliver to it last failed."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"WebhookEndpointWithSecret":{"description":"A webhook endpoint with its plain signing secret — only in the response that created the secret.","allOf":[{"$ref":"#/components/schemas/WebhookEndpoint"},{"type":"object","required":["secret"],"properties":{"secret":{"type":"string","description":"The signing secret, starting `whsec_`. Shown once: store it now. Use the whole string, prefix included, as the HMAC key."}}}]},"WebhookEndpointEnvelope":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WebhookEndpoint"}},"additionalProperties":false},"WebhookEndpointCreate":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri","maxLength":1024,"description":"An https URL on the public internet. Refused with a 422: plain http; an address that is loopback, private, link-local (including 169.254.169.254) or otherwise reserved, also when written as IPv6; and names that only exist inside a network, such as `localhost`, names without a dot and names under `.local` or `.internal`."},"events":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/WebhookEventName"},"description":"The events to receive, or `[\"*\"]` for all."},"description":{"type":["string","null"],"maxLength":255}},"additionalProperties":false},"WebhookEndpointUpdate":{"type":"object","description":"Only the fields sent are changed.","properties":{"url":{"type":"string","format":"uri","maxLength":1024,"description":"The same rules as on create: https, on the public internet."},"events":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/WebhookEventName"},"description":"Replaces the whole list."},"description":{"type":["string","null"],"maxLength":255,"description":"`null` clears it."},"status":{"type":"string","enum":["active","paused"]}},"additionalProperties":false},"WebhookDelivery":{"type":"object","description":"One event queued for one endpoint, and how its attempts went.","required":["id","endpoint_id","event","event_id","status","attempts","response_status","response_body","error","payload","next_retry_at","delivered_at","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"Sent as the X-Delivery-Id header."},"endpoint_id":{"type":"string","format":"uuid"},"event":{"type":"string","description":"The event name; `ping` for a test event."},"event_id":{"type":"string","description":"The body's `event_id`."},"status":{"type":"string","enum":["pending","succeeded","failed","abandoned"],"description":"`pending`: waiting for its first attempt or a retry. `succeeded`: your endpoint answered 2xx. `abandoned`: all 7 attempts failed, or the endpoint's host resolved to a private or reserved address and nothing was sent (see `error`). `failed`: momentarily, between a failed attempt and scheduling the next."},"attempts":{"type":"integer","minimum":0,"maximum":7,"description":"Attempts made so far."},"response_status":{"type":["integer","null"],"description":"The HTTP status of the last answer, or null if none came."},"response_body":{"type":["string","null"],"description":"The first 2,000 characters of the last answer's body."},"error":{"type":["string","null"],"description":"Why the last attempt got no answer (timeout, DNS, TLS, refused connection), or why it was not sent (the host resolved to a private or reserved address)."},"payload":{"type":"object","additionalProperties":true,"description":"The event's `data`, exactly as sent."},"next_retry_at":{"type":["string","null"],"format":"date-time","description":"When the next attempt runs, while `pending` after a failure."},"delivered_at":{"type":["string","null"],"format":"date-time","description":"When it succeeded."},"created_at":{"type":"string","format":"date-time","description":"When the event was queued — the body's `created`."},"updated_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"WebhookEvent":{"type":"object","description":"The body of every webhook request. `data` depends on `event`; see each event.","required":["event","event_id","created","data"],"properties":{"event":{"type":"string","description":"The event name, e.g. `deal.won`. Also sent as the X-Event header."},"event_id":{"type":"string","description":"`evt_` followed by a UUID. Unique per event; the same for every endpoint that receives it and for every retry. Deduplicate on it. Also sent as the X-Event-Id header."},"created":{"type":"integer","description":"Unix time in seconds at which the event was queued. The same on every retry (the time of each attempt is the signature's `t`)."},"data":{"type":"object","additionalProperties":true,"description":"The event's payload."}},"additionalProperties":false},"WebhookDealPayload":{"type":"object","description":"The deal, as it is after the event.","required":["id","title","value_cents","currency","stage","status","owner_id","account_id"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"value_cents":{"type":"integer","description":"Deal value in the currency's minor unit (öre)."},"currency":{"type":"string","description":"ISO 4217, e.g. `SEK`."},"stage":{"$ref":"#/components/schemas/WebhookDealStage"},"status":{"type":"string","enum":["open","won","lost","archived"]},"owner_id":{"type":["string","null"],"format":"uuid","description":"The member who owns the deal."},"account_id":{"type":["string","null"],"format":"uuid","description":"The customer company."}},"additionalProperties":false},"WebhookDealStage":{"type":"string","enum":["draft","ready_to_send","sent","opened","engaged","in_negotiation","proposal_approved","contract_sent","contract_signed"]},"WebhookDealStageChangedPayload":{"allOf":[{"$ref":"#/components/schemas/WebhookDealPayload"},{"type":"object","required":["from_stage","to_stage","reason"],"properties":{"from_stage":{"$ref":"#/components/schemas/WebhookDealStage"},"to_stage":{"$ref":"#/components/schemas/WebhookDealStage"},"reason":{"type":["string","null"],"description":"Why it moved, when someone said."}}}]},"WebhookDealLostPayload":{"allOf":[{"$ref":"#/components/schemas/WebhookDealPayload"},{"type":"object","required":["reason"],"properties":{"reason":{"type":["string","null"],"description":"Why the deal was lost, as the seller wrote it."}}}]},"WebhookRoomPayload":{"type":"object","required":["id","slug","title","status","deal_id","published_at"],"properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string","description":"The room's short code in the buyer's link."},"title":{"type":"string"},"status":{"type":"string","enum":["draft","published","expired","archived"]},"deal_id":{"type":["string","null"],"format":"uuid"},"published_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"WebhookStakeholderPayload":{"type":"object","required":["id","deal_room_id","email","name","role"],"properties":{"id":{"type":"string","format":"uuid"},"deal_room_id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"name":{"type":["string","null"]},"role":{"type":"string","enum":["champion","economic","technical","user","legal","procurement","other"],"description":"Their part in the buying decision."}},"additionalProperties":false},"WebhookMessagePayload":{"type":"object","required":["id","thread_id","deal_room_id","document_id","author_type","author_id","author_name","body","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"thread_id":{"type":"string","format":"uuid"},"deal_room_id":{"type":["string","null"],"format":"uuid","description":"Set for a deal room's chat."},"document_id":{"type":["string","null"],"format":"uuid","description":"Set for a document's team chat."},"author_type":{"type":"string","enum":["user","stakeholder"],"description":"`user`: a member of your organization. `stakeholder`: someone on the buyer's side."},"author_id":{"type":"string","format":"uuid","description":"The member's or the stakeholder's id."},"author_name":{"type":["string","null"]},"body":{"type":"string","description":"The message, as plain text (up to 4,000 characters)."},"created_at":{"type":"string","format":"date-time"}},"additionalProperties":false},"WebhookQuotePayload":{"type":"object","required":["id","quote_number","title","deal_id","deal_room_id","total_cents","currency","accepted_at"],"properties":{"id":{"type":"string","format":"uuid"},"quote_number":{"type":"string"},"title":{"type":"string"},"deal_id":{"type":["string","null"],"format":"uuid"},"deal_room_id":{"type":["string","null"],"format":"uuid"},"total_cents":{"type":"integer","description":"The accepted total, VAT included, in the currency's minor unit."},"currency":{"type":"string"},"accepted_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"WebhookRecipient":{"type":"object","description":"Someone a document was sent to.","required":["id","name","email","role"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["signer","approver","cc","viewer"],"description":"`signer` and `approver` must sign; `cc` and `viewer` only receive the document."}},"additionalProperties":false},"WebhookSignatureRequestPayload":{"type":"object","description":"A document sent for signing (a signature request).","required":["id","document_id","deal_room_id","title","status"],"properties":{"id":{"type":"string","format":"uuid","description":"The signature request's id."},"document_id":{"type":"string","format":"uuid"},"deal_room_id":{"type":["string","null"],"format":"uuid"},"title":{"type":"string"},"status":{"type":"string","enum":["draft","sent","partially_signed","completed","declined","expired","canceled"]}},"additionalProperties":false},"WebhookSignatureRequestedPayload":{"allOf":[{"$ref":"#/components/schemas/WebhookSignatureRequestPayload"},{"type":"object","required":["sent_at","expires_at","recipients"],"properties":{"sent_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time","description":"After this the document can no longer be signed."},"recipients":{"type":"array","description":"In signing order.","items":{"$ref":"#/components/schemas/WebhookRecipient"}}}}]},"WebhookSignatureCompletedPayload":{"allOf":[{"$ref":"#/components/schemas/WebhookSignatureRequestPayload"},{"type":"object","required":["completed_at"],"properties":{"completed_at":{"type":["string","null"],"format":"date-time"}}}]},"WebhookSignatureCanceledPayload":{"allOf":[{"$ref":"#/components/schemas/WebhookSignatureRequestPayload"},{"type":"object","required":["reason","canceled_at"],"properties":{"reason":{"type":["string","null"]},"canceled_at":{"type":["string","null"],"format":"date-time"}}}]},"WebhookRecipientActivityPayload":{"type":"object","description":"What one recipient did with a document sent for signing.","required":["signature_request_id","document_id","deal_room_id","title","recipient"],"properties":{"signature_request_id":{"type":"string","format":"uuid"},"document_id":{"type":"string","format":"uuid"},"deal_room_id":{"type":["string","null"],"format":"uuid"},"title":{"type":"string"},"recipient":{"$ref":"#/components/schemas/WebhookRecipient"}},"additionalProperties":false},"WebhookDocumentViewedPayload":{"allOf":[{"$ref":"#/components/schemas/WebhookRecipientActivityPayload"},{"type":"object","required":["opened_at"],"properties":{"opened_at":{"type":["string","null"],"format":"date-time"}}}]},"WebhookSignerSignedPayload":{"allOf":[{"$ref":"#/components/schemas/WebhookRecipientActivityPayload"},{"type":"object","required":["signed_at"],"properties":{"signed_at":{"type":["string","null"],"format":"date-time"}}}]},"WebhookWorkOrderPayload":{"type":"object","required":["id","order_number","title","status","account_id","agreed_price_cents","currency","actual_start","actual_end","completed_at","customer_signed_at"],"properties":{"id":{"type":"string","format":"uuid"},"order_number":{"type":"string"},"title":{"type":"string"},"status":{"type":"string","enum":["booked","scheduled","in_progress","completed","invoiced","cancelled"]},"account_id":{"type":"string","format":"uuid","description":"The customer."},"agreed_price_cents":{"type":["integer","null"],"description":"A fixed price agreed for the job, or null when it is billed by time and materials."},"currency":{"type":"string"},"actual_start":{"type":["string","null"],"format":"date-time"},"actual_end":{"type":["string","null"],"format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"},"customer_signed_at":{"type":["string","null"],"format":"date-time","description":"When the customer signed for the work on site, if they did."}},"additionalProperties":false},"WebhookInvoicePayload":{"type":"object","required":["id","invoice_number","series","type","status","account_id","credited_invoice_id","currency","issue_date","due_date","net_cents","vat_cents","total_cents","ocr_reference"],"properties":{"id":{"type":"string","format":"uuid"},"invoice_number":{"type":["string","null"]},"series":{"type":["string","null"]},"type":{"type":"string","enum":["invoice","credit_note"]},"status":{"type":"string","enum":["draft","sent","partially_paid","paid","overdue","credited","cancelled","written_off"]},"account_id":{"type":"string","format":"uuid","description":"The customer."},"credited_invoice_id":{"type":["string","null"],"format":"uuid","description":"For a credit note, the invoice it credits."},"currency":{"type":"string"},"issue_date":{"type":["string","null"],"format":"date"},"due_date":{"type":["string","null"],"format":"date"},"net_cents":{"type":"integer","description":"Excluding VAT."},"vat_cents":{"type":"integer"},"total_cents":{"type":"integer","description":"To pay, VAT and rounding included."},"ocr_reference":{"type":["string","null"],"description":"The OCR number to pay with."}},"additionalProperties":false},"WebhookQuoteHandoverWarning":{"type":"string","description":"Something the handover from the quote could not fill in. New codes may be added; treat an unknown one as a note for a person to read.","enum":["service_address_missing","service_address_ambiguous","deduction_not_included","tax_profile_missing","recurring_lines","no_labour_lines","no_lines"]},"WebhookWorkOrderFromQuotePayload":{"type":"object","required":["id","order_number","title","status","account_id","contact_id","service_address_id","rot_rut","currency","quote_id","quote_number","created_automatically","warnings","created_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The work order."},"order_number":{"type":"string"},"title":{"type":"string","description":"The quote's title."},"status":{"type":"string","enum":["booked"],"description":"Always `booked`: waiting to be planned, with no time on it."},"account_id":{"type":"string","format":"uuid","description":"The customer: the quote's deal's, or the customer of the contact who accepted."},"contact_id":{"type":["string","null"],"format":"uuid","description":"The customer's contact who accepted the quote, when they are one."},"service_address_id":{"type":["string","null"],"format":"uuid","description":"The customer's default (or only) service address; null with a `service_address_*` warning when there is none to choose."},"rot_rut":{"type":"string","enum":["none","rot","rut"],"description":"The quote's deduction choice."},"currency":{"type":"string"},"quote_id":{"type":"string","format":"uuid","description":"The accepted quote."},"quote_number":{"type":"string"},"created_automatically":{"type":"boolean","description":"True when the buyer's acceptance made it; false when a person or an API call did."},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/WebhookQuoteHandoverWarning"}},"created_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"WebhookInvoiceFromQuotePayload":{"type":"object","required":["id","status","account_id","contact_id","currency","net_cents","vat_cents","total_cents","rot_rut","rot_rut_requested_cents","quote_id","quote_number","created_automatically","warnings","created_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The invoice draft."},"status":{"type":"string","enum":["draft"],"description":"Always `draft`: no number, not sent."},"account_id":{"type":"string","format":"uuid","description":"The customer."},"contact_id":{"type":["string","null"],"format":"uuid"},"currency":{"type":"string"},"net_cents":{"type":"integer","description":"Excluding VAT."},"vat_cents":{"type":"integer"},"total_cents":{"type":"integer","description":"VAT and öresavrundning included, before any deduction."},"rot_rut":{"type":"string","enum":["none","rot","rut"]},"rot_rut_requested_cents":{"type":"integer","description":"The deduction the draft claims; the customer pays `total_cents` less this."},"quote_id":{"type":"string","format":"uuid","description":"The accepted quote."},"quote_number":{"type":"string"},"created_automatically":{"type":"boolean","description":"True when the buyer's acceptance made it; false when a person or an API call did."},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/WebhookQuoteHandoverWarning"}},"created_at":{"type":["string","null"],"format":"date-time"}},"additionalProperties":false},"WebhookPingPayload":{"type":"object","required":["endpoint_id","message"],"properties":{"endpoint_id":{"type":"string","format":"uuid","description":"The endpoint the test was sent to."},"message":{"type":"string"}},"additionalProperties":false},"WorkOrder":{"type":"object","description":"A job (uppdrag): what is to be done, for which customer, where and when, and by whom. Which nested parts are included depends on the endpoint — see each property.","required":["id","order_number","title","description","status","priority","source","handover","customer","pricing","schedule","completed_at","cancelled_at","cancel_reason","customer_signed_at","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The work order id."},"order_number":{"type":"string","description":"Number from the organization's work order series, unique within the organization (format set in the panel, e.g. `1042` or `WO-01042`)."},"title":{"type":"string","description":"What the job is, as shown on the board."},"description":{"type":["string","null"],"description":"Free text for the people doing the job."},"status":{"type":"string","enum":["booked","scheduled","in_progress","completed","invoiced","cancelled"],"description":"`booked`: promised to the customer, nobody placed yet. `scheduled`: somebody is going. `in_progress`: they are there. `completed`: the work is done (and billable). `invoiced`: the money has been asked for. `cancelled`: not happening."},"priority":{"type":"integer","description":"-2 (lowest) to 2 (highest); 0 is normal.","minimum":-2,"maximum":2},"source":{"type":"string","description":"Where the job came from: `manual` (created in the panel or this API), `recurrence` (written by a recurrence) or `quote` (made from an accepted quote — see `handover`). More values may be added."},"handover":{"description":"The accepted quote this work order was made from, or null when it was not.","oneOf":[{"$ref":"#/components/schemas/QuoteHandoverOrigin"},{"type":"null"}]},"customer":{"$ref":"#/components/schemas/WorkOrderCustomer"},"contact":{"description":"The customer contact for this job, or null. Included by `GET /work-orders/{workOrder}` only.","oneOf":[{"$ref":"#/components/schemas/WorkOrderContact"},{"type":"null"}]},"service_address":{"description":"Where the work is done, or null. Included by the list, `GET /work-orders/{workOrder}` and `POST /work-orders/{workOrder}/complete`.","oneOf":[{"$ref":"#/components/schemas/WorkOrderServiceAddress"},{"type":"null"}]},"pricing":{"$ref":"#/components/schemas/WorkOrderPricing"},"schedule":{"$ref":"#/components/schemas/WorkOrderSchedule"},"assignments":{"type":"array","description":"Who is going. Included everywhere except the response to creating a work order.","items":{"$ref":"#/components/schemas/WorkOrderAssignment"}},"services":{"type":"array","description":"Services ordered for the job, priced as they were when ordered. Included by `GET /work-orders/{workOrder}` only.","items":{"$ref":"#/components/schemas/WorkOrderServiceLine"}},"checklist":{"type":"array","description":"The steps to tick, in `sort_order`. Included by `GET /work-orders/{workOrder}` and `POST /work-orders/{workOrder}/complete`.","items":{"$ref":"#/components/schemas/ChecklistItem"}},"materials":{"type":"array","description":"Materials used. Included by `GET /work-orders/{workOrder}` and `POST /work-orders/{workOrder}/complete`; `GET /work-orders/{workOrder}/materials` returns the same lines with `net_cents`.","items":{"$ref":"#/components/schemas/WorkOrderMaterialRow"}},"completed_at":{"type":["string","null"],"format":"date-time","description":"When the job was completed, or null."},"cancelled_at":{"type":["string","null"],"format":"date-time","description":"When the job was cancelled, or null."},"cancel_reason":{"type":["string","null"],"description":"The `reason` given when cancelling, or null."},"customer_signed_at":{"type":["string","null"],"format":"date-time","description":"When the customer signed on completion, or null."},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"updated_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."}},"additionalProperties":false},"WorkOrderCustomer":{"type":"object","description":"The customer the job is for (a customer from the Customers API). Still shown after that customer is deleted: a deleted customer keeps its past jobs.","required":["id","name","customer_number"],"properties":{"id":{"type":"string","format":"uuid","description":"The customer id (`account_id` in requests)."},"name":{"type":"string","description":"Customer name."},"customer_number":{"type":["string","null"],"description":"The customer number, or null. Always null in the responses of `POST /schedule/assign` and `POST /schedule/move`."}},"additionalProperties":false},"WorkOrderContact":{"type":"object","description":"A contact person at the customer, as stored.","required":["id","organization_id","account_id","email","name","title","phone","crm_external_id","crm_provider","metadata","created_at","updated_at","deleted_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The contact id (`contact_id` in requests)."},"organization_id":{"type":"string","format":"uuid","description":"Your organization."},"account_id":{"type":["string","null"],"format":"uuid","description":"The customer the contact belongs to, or null."},"email":{"type":["string","null"],"description":"Email address."},"name":{"type":["string","null"],"description":"Full name."},"title":{"type":["string","null"],"description":"Job title or role, e.g. \"Ordförande\"."},"phone":{"type":["string","null"],"description":"Phone number as entered."},"crm_external_id":{"type":["string","null"],"description":"Id in a connected CRM, or null."},"crm_provider":{"type":["string","null"],"description":"Which CRM that id belongs to, or null."},"metadata":{"type":["object","null"],"description":"Free-form data, or null.","additionalProperties":true},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"updated_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"deleted_at":{"type":["string","null"],"format":"date-time","description":"Set when the contact was deleted; deleted contacts are not shown, so always null here."}},"additionalProperties":false},"WorkOrderServiceAddress":{"type":"object","description":"A customer address, as stored. `lat`, `lng` and `geofence_radius_m` decide whether a clock-in counts as on site.","required":["id","organization_id","account_id","type","label","street","street2","zip","city","country","lat","lng","geofence_radius_m","access_notes","contact_id","is_default","metadata","created_at","updated_at","deleted_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The address id (`service_address_id` in requests)."},"organization_id":{"type":"string","format":"uuid","description":"Your organization."},"account_id":{"type":"string","format":"uuid","description":"The customer the address belongs to."},"type":{"type":"string","description":"Kind of address, e.g. `service` or `billing`."},"label":{"type":["string","null"],"description":"A name for the address, e.g. \"Lägenhet 1102\"."},"street":{"type":["string","null"],"description":"Street and number."},"street2":{"type":["string","null"],"description":"Second address line."},"zip":{"type":["string","null"],"description":"Postal code as entered, e.g. \"753 20\"."},"city":{"type":["string","null"],"description":"City."},"country":{"type":["string","null"],"description":"ISO 3166-1 alpha-2 country code."},"lat":{"type":["number","null"],"description":"Latitude, WGS 84."},"lng":{"type":["number","null"],"description":"Longitude, WGS 84."},"geofence_radius_m":{"type":["integer","null"],"description":"Radius in metres within which a clock-in counts as on site, or null for no check."},"access_notes":{"type":["string","null"],"description":"Door codes, where the key is, parking."},"contact_id":{"type":["string","null"],"format":"uuid","description":"Contact on site, or null."},"is_default":{"type":"boolean","description":"Whether this is the customer's default address."},"metadata":{"type":["object","null"],"description":"Free-form data, or null.","additionalProperties":true},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"updated_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"deleted_at":{"type":["string","null"],"format":"date-time","description":"Always null here: deleted addresses are not shown."}},"additionalProperties":false},"WorkOrderPricing":{"type":"object","required":["model","agreed_price_cents","currency","rot_rut","rot_rut_share"],"properties":{"model":{"type":"string","enum":["hourly","fixed","per_unit"],"description":"How the customer is charged: by approved hours, a fixed `agreed_price_cents`, or per unit of the ordered services."},"agreed_price_cents":{"type":["integer","null"],"description":"The agreed price for a `fixed` job, in öre excluding VAT, or null."},"currency":{"type":"string","description":"ISO 4217 currency code. Always `SEK` today."},"rot_rut":{"type":"string","enum":["none","rot","rut"],"description":"Swedish tax deduction on the labour: ROT (repairs, 30 %) or RUT (household services, 50 %), computed on the invoice basis."},"rot_rut_share":{"type":["number","null"],"description":"Share of the deduction this customer claims, in percent (two owners of a home claim 50 each), or null for all of it.","minimum":0,"maximum":100}},"additionalProperties":false},"WorkOrderSchedule":{"type":"object","description":"When the job is planned, what was promised, and when it actually ran. All UTC.","required":["planned_start","planned_end","earliest_start","latest_start","estimated_minutes","actual_start","actual_end","actual_minutes"],"properties":{"planned_start":{"type":["string","null"],"format":"date-time","description":"When the job is planned to start, or null for unplanned work."},"planned_end":{"type":["string","null"],"format":"date-time","description":"When it is planned to end, or null."},"earliest_start":{"type":["string","null"],"format":"date-time","description":"The customer's window: earliest acceptable start, or null. Not the plan — \"any time Tuesday morning\" rather than \"at nine\"."},"latest_start":{"type":["string","null"],"format":"date-time","description":"The customer's window: latest acceptable start, or null."},"estimated_minutes":{"type":["integer","null"],"description":"Expected duration in minutes, or null."},"actual_start":{"type":["string","null"],"format":"date-time","description":"When the work started: set when the job moves to `in_progress`, or corrected on completion."},"actual_end":{"type":["string","null"],"format":"date-time","description":"When the work ended: set on completion."},"actual_minutes":{"type":["integer","null"],"description":"Minutes between `actual_start` and `actual_end`, or null until both are known."}},"additionalProperties":false},"WorkOrderAssignment":{"type":"object","description":"One employee on a job.","required":["id","role","status","notified_at","accepted_at","employee"],"properties":{"id":{"type":"string","format":"uuid","description":"The assignment id, for `DELETE /work-orders/{workOrder}/assignments/{assignment}`."},"role":{"type":"string","enum":["lead","helper"],"description":"`lead` is answerable for the job; `helper`s come along."},"status":{"type":"string","description":"`assigned`."},"notified_at":{"type":["string","null"],"format":"date-time","description":"When the employee was told (email, and SMS if the organization has turned it on), or null."},"accepted_at":{"type":["string","null"],"format":"date-time","description":"When the employee accepted, or null."},"employee":{"description":"The employee, or null if they have since been deleted.","oneOf":[{"type":"object","required":["id","name","color"],"properties":{"id":{"type":"string","format":"uuid","description":"The employee id."},"name":{"type":"string","description":"First and last name."},"color":{"type":["string","null"],"description":"Colour on the board, e.g. `#2F80ED`, or null."}},"additionalProperties":false},{"type":"null"}]}},"additionalProperties":false},"WorkOrderServiceLine":{"type":"object","description":"A service ordered for the job. Name and price are copied from the catalogue when ordered, so later price changes do not rewrite it.","required":["id","organization_id","work_order_id","service_id","name","quantity","unit_price_cents","discount_percent","vat_rate","rot_rut_eligible","sort_order","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The line id."},"organization_id":{"type":"string","format":"uuid","description":"Your organization."},"work_order_id":{"type":"string","format":"uuid","description":"The work order."},"service_id":{"type":["string","null"],"format":"uuid","description":"The catalogue service it was ordered from, or null if that service was deleted."},"name":{"type":"string","description":"Service name as ordered."},"quantity":{"type":"number","description":"Hours or units."},"unit_price_cents":{"type":"integer","description":"Amount in öre (1 SEK = 100 öre), excluding VAT. Per hour or unit."},"discount_percent":{"type":"number","minimum":0,"maximum":100,"description":"Discount on the line in percent, up to two decimals. 0 unless the line was carried over from an accepted quote, where it is the quote's line discount compounded with its quote discount (10 % on a line already 25 % off is 32.5)."},"vat_rate":{"type":"number","description":"VAT in percent."},"rot_rut_eligible":{"type":"boolean","description":"Whether the service qualifies for ROT/RUT."},"sort_order":{"type":"integer","description":"Position on the job."},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"updated_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."}},"additionalProperties":false},"ChecklistItem":{"type":"object","description":"A step on a job. A job cannot be completed while a required step is not done.","required":["id","organization_id","work_order_id","label","is_done","is_required","done_by","done_at","sort_order","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The checklist item id."},"organization_id":{"type":"string","format":"uuid","description":"Your organization."},"work_order_id":{"type":"string","format":"uuid","description":"The work order it belongs to."},"label":{"type":"string","description":"What to do."},"is_done":{"type":"boolean","description":"Whether it has been ticked."},"is_required":{"type":"boolean","description":"Whether the job can be completed without it."},"done_by":{"type":["string","null"],"format":"uuid","description":"The member who ticked it (for an API call, the member the key acts as), or null."},"done_at":{"type":["string","null"],"format":"date-time","description":"When it was ticked, or null. Set by Closepact when `is_done` changes; never accepted from a request."},"sort_order":{"type":"integer","description":"Position in the list, from 0."},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"updated_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."}},"additionalProperties":false},"WorkOrderMaterial":{"type":"object","description":"A line of material used on a job, as the invoice will read it.","required":["id","work_order_id","name","quantity","unit","unit_price_cents","discount_percent","vat_rate","rot_rut_eligible","net_cents","added_by","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The material line id."},"work_order_id":{"type":"string","format":"uuid","description":"The work order it belongs to."},"name":{"type":"string","description":"What was used."},"quantity":{"type":"number","description":"How much, up to three decimals (2.5 metres of cable)."},"unit":{"type":["string","null"],"description":"Unit as written, e.g. `st`, `m`, `l`, or null."},"unit_price_cents":{"type":"integer","description":"Amount in öre (1 SEK = 100 öre), excluding VAT. Per unit."},"discount_percent":{"type":"number","minimum":0,"maximum":100,"description":"Discount on the line in percent, up to two decimals. 0 unless the line was carried over from an accepted quote."},"vat_rate":{"type":"number","description":"VAT in percent."},"rot_rut_eligible":{"type":"boolean","description":"Whether the line counts toward ROT/RUT. False unless set: the deduction applies to labour, and the invoice basis never applies it to materials."},"net_cents":{"type":"integer","description":"`quantity × unit_price_cents` less `discount_percent`, rounded once to whole öre, excluding VAT."},"added_by":{"type":["string","null"],"format":"uuid","description":"The member who added the line (for an API call, the member the key acts as), or null."},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"updated_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."}},"additionalProperties":false},"WorkOrderMaterialRow":{"type":"object","description":"A material line as stored, embedded in a work order (no `net_cents`; see `WorkOrderMaterial`).","required":["id","organization_id","work_order_id","name","quantity","unit","unit_price_cents","discount_percent","vat_rate","rot_rut_eligible","added_by","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","description":"The material line id."},"organization_id":{"type":"string","format":"uuid","description":"Your organization."},"work_order_id":{"type":"string","format":"uuid","description":"The work order it belongs to."},"name":{"type":"string","description":"What was used."},"quantity":{"type":"number","description":"How much, up to three decimals (2.5 metres of cable)."},"unit":{"type":["string","null"],"description":"Unit as written, e.g. `st`, `m`, `l`, or null."},"unit_price_cents":{"type":"integer","description":"Amount in öre (1 SEK = 100 öre), excluding VAT. Per unit."},"discount_percent":{"type":"number","minimum":0,"maximum":100,"description":"Discount on the line in percent, up to two decimals. 0 unless the line was carried over from an accepted quote."},"vat_rate":{"type":"number","description":"VAT in percent."},"rot_rut_eligible":{"type":"boolean","description":"Whether the line counts toward ROT/RUT. False unless set: the deduction applies to labour, and the invoice basis never applies it to materials."},"added_by":{"type":["string","null"],"format":"uuid","description":"The member who added the line, or null."},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."},"updated_at":{"type":"string","format":"date-time","description":"ISO 8601 date-time, in UTC."}},"additionalProperties":false},"WorkOrderInvoiceBasis":{"type":"object","description":"What a job is worth, line by line, so that it can be invoiced here or elsewhere. Only approved time is billed; labour and materials are separate lines; each line is rounded once and the totals are sums of the rounded lines.","required":["work_order","already_invoiced","lines","totals","rot_rut","time","warnings"],"properties":{"work_order":{"type":"object","required":["id","order_number","title","status","price_model","currency","agreed_price_cents","completed_at","planned_start","account"],"properties":{"id":{"type":"string","format":"uuid","description":"The work order id."},"order_number":{"type":"string","description":"The work order number."},"title":{"type":"string","description":"The job title."},"status":{"type":"string","enum":["booked","scheduled","in_progress","completed","invoiced","cancelled"],"description":"`booked`: promised to the customer, nobody placed yet. `scheduled`: somebody is going. `in_progress`: they are there. `completed`: the work is done (and billable). `invoiced`: the money has been asked for. `cancelled`: not happening."},"price_model":{"type":"string","enum":["hourly","fixed","per_unit"],"description":"How the customer is charged: by approved hours, a fixed `agreed_price_cents`, or per unit of the ordered services."},"currency":{"type":"string","description":"ISO 4217 code, `SEK`."},"agreed_price_cents":{"type":["integer","null"],"description":"The agreed fixed price in öre, or null."},"completed_at":{"type":["string","null"],"format":"date-time","description":"When the job was completed, or null."},"planned_start":{"type":["string","null"],"format":"date-time","description":"Planned start, or null."},"account":{"type":"object","description":"The customer.","required":["id","name","customer_number"],"properties":{"id":{"type":"string","format":"uuid","description":"The customer id."},"name":{"type":["string","null"],"description":"Customer name, or null if the customer was deleted."},"customer_number":{"type":["string","null"],"description":"Customer number, or null."}},"additionalProperties":false}},"additionalProperties":false},"already_invoiced":{"type":"boolean","description":"True when the job is `invoiced`: the basis then shows what was billed."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/WorkOrderInvoiceBasisLine"}},"totals":{"type":"object","description":"Sums over the billed lines, in öre. Labour includes a fixed price. `labour_cost_cents` is the exception: what the approved hours cost, for margin.","required":["labour_net_cents","labour_vat_cents","materials_net_cents","materials_vat_cents","net_cents","vat_cents","gross_cents","labour_cost_cents"],"properties":{"labour_net_cents":{"type":"integer","description":"Labour excluding VAT."},"labour_vat_cents":{"type":"integer","description":"VAT on labour."},"materials_net_cents":{"type":"integer","description":"Materials excluding VAT."},"materials_vat_cents":{"type":"integer","description":"VAT on materials."},"net_cents":{"type":"integer","description":"Everything excluding VAT."},"vat_cents":{"type":"integer","description":"All VAT."},"gross_cents":{"type":"integer","description":"Everything including VAT."},"labour_cost_cents":{"type":"integer","description":"What all approved hours on the job cost — billed or not, e.g. the hours behind a fixed price — from each person's cost rate at approval (`hourly_rate_cents`). Not invoiced; `labour_net_cents` less this is the labour margin."}},"additionalProperties":false},"rot_rut":{"type":"object","description":"The ROT/RUT deduction, computed on the eligible labour including VAT. The buyer's annual ceiling is not applied (`annual_ceiling_applied` is always false): it depends on what other companies have claimed for them this year.","required":["type","rate_percent","share_percent","eligible_labour_net_cents","eligible_labour_gross_cents","claim_base_cents","deduction_cents","customer_pays_cents","annual_ceiling_applied"],"properties":{"type":{"type":"string","enum":["none","rot","rut"],"description":"Swedish tax deduction on the labour: ROT (repairs, 30 %) or RUT (household services, 50 %), computed on the invoice basis."},"rate_percent":{"type":["integer","null"],"description":"30 for ROT, 50 for RUT, null for none."},"share_percent":{"type":["number","null"],"description":"The job's `rot_rut_share`, or null."},"eligible_labour_net_cents":{"type":"integer","description":"Eligible labour excluding VAT."},"eligible_labour_gross_cents":{"type":"integer","description":"Eligible labour including VAT."},"claim_base_cents":{"type":"integer","description":"The gross narrowed by `share_percent`."},"deduction_cents":{"type":"integer","description":"`claim_base_cents × rate_percent`, rounded once."},"customer_pays_cents":{"type":"integer","description":"`totals.gross_cents` less the deduction."},"annual_ceiling_applied":{"type":"boolean","description":"Always false."}},"additionalProperties":false},"time":{"type":"object","required":["approved_minutes","awaiting_approval_minutes","awaiting_approval_entries"],"properties":{"approved_minutes":{"type":"integer","description":"Approved minutes on the job (all of them, billed or not)."},"awaiting_approval_minutes":{"type":"integer","description":"Reported minutes not yet approved, and therefore not on this basis."},"awaiting_approval_entries":{"type":"integer","description":"How many time entries those are."}},"additionalProperties":false},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/BookingWarning"}}},"additionalProperties":false},"WorkOrderInvoiceBasisLine":{"type":"object","required":["kind","description","detail","quantity","unit_price_cents","discount_percent","net_cents","vat_rate","vat_cents","rot_rut_eligible","billed","excluded_reason","source"],"properties":{"kind":{"type":"string","enum":["labour","fixed_price","material"],"description":"`labour`: approved hours per person and kind of time, or an ordered per-unit service. `fixed_price`: the agreed price. `material`: a material line."},"description":{"type":"string","description":"Employee name, service name, \"Agreed price\" or the material."},"detail":{"type":["string","null"],"description":"Kind of time (`work`, `travel`, `overtime`) for hours, the unit for materials, otherwise null."},"quantity":{"type":"number","description":"Hours (three decimals) or units."},"unit_price_cents":{"type":"integer","description":"Amount in öre (1 SEK = 100 öre), excluding VAT. Per hour or unit."},"discount_percent":{"type":"number","minimum":0,"maximum":100,"description":"The line's discount in percent, already taken off `net_cents`. 0 except on an ordered service or a material carried over from a discounted quote."},"net_cents":{"type":"integer","description":"Amount in öre (1 SEK = 100 öre), excluding VAT. 0 for a line that is not billed."},"vat_rate":{"type":"number","description":"VAT in percent."},"vat_cents":{"type":"integer","description":"VAT on `net_cents`, in öre."},"rot_rut_eligible":{"type":"boolean","description":"Whether the line counts toward the deduction (labour only)."},"billed":{"type":"boolean","description":"False for lines shown for reference: hours behind a fixed or per-unit price, and time marked not billable."},"excluded_reason":{"type":["string","null"],"enum":["fixed_price","per_unit_price","not_billable",null],"description":"Why a line is not billed, or null."},"source":{"type":"object","description":"What the line was computed from.","required":["type"],"properties":{"type":{"type":"string","enum":["time_entries","agreed_price","ordered_service","material"],"description":"The kind of source."},"employee_id":{"type":["string","null"],"format":"uuid","description":"For `time_entries`: whose hours."},"minutes":{"type":"integer","description":"For `time_entries`: approved minutes."},"entries":{"type":"integer","description":"For `time_entries`: how many entries."},"work_order_service_id":{"type":"string","format":"uuid","description":"For `ordered_service`: the service line."},"work_order_material_id":{"type":"string","format":"uuid","description":"For `material`: the material line."},"rate_basis":{"type":"string","enum":["agreed_price","employee_rate","none"],"description":"For `time_entries`: where the price per hour came from — the one service price per hour the job was quoted (`agreed_price`), the person's billing rate copied at approval (`employee_rate`), or neither, in which case the hours are billed at nothing and the basis warns (`none`). Never the cost rate."},"cost_cents":{"type":["integer","null"],"description":"For `time_entries`: what these hours cost, from each entry's cost rate at approval; null when no cost rate was recorded. Not invoiced."}},"additionalProperties":false}},"additionalProperties":false},"BookingWarning":{"type":"object","description":"Something to look at before invoicing. Codes: `time_awaiting_approval`, `not_completed`, `already_invoiced`, `fixed_price_missing`, `fixed_price_with_materials`, `labour_rate_missing` (hours with no billing rate, billed at nothing), `labour_billed_at_cost` (hours billed at a rate equal to their cost); more may be added.","required":["code","message","context"],"properties":{"code":{"type":"string","description":"Stable code."},"message":{"type":"string","description":"Explanation, in English."},"context":{"type":"object","description":"Figures behind the warning, e.g. `{\"minutes\": 120, \"entries\": 2}`; empty when there are none.","additionalProperties":true}},"additionalProperties":false},"ScheduleFinding":{"type":"object","description":"Something wrong with sending an employee to a job at a time. `error`s stop an assignment or move unless `force` is true; `warning`s never do.","required":["code","severity","message","context"],"properties":{"code":{"type":"string","enum":["double_booked","not_employed","absent","outside_working_hours"],"description":"`double_booked` (error): already on another non-cancelled job that overlaps; jobs that only touch (one ends 12:00, the next starts 12:00) do not. `not_employed` (error): inactive, or outside `started_at`–`ended_at` (both days included). `absent` (warning): an absence that is not rejected overlaps. `outside_working_hours` (warning): the employee has availability rules and the job falls outside them. All but `double_booked` are judged on the Swedish clock: the job's UTC times are converted to `Europe/Stockholm` before they are compared with working hours, absences and employment dates."},"severity":{"type":"string","enum":["error","warning"],"description":"Whether it blocks."},"message":{"type":"string","description":"Explanation, in English, naming the employee."},"context":{"type":"object","description":"Details by code. `double_booked`: `work_order_id`, `order_number`, `title`, `planned_start`, `planned_end` of the other job. `absent`: `absence_id`, `type`, `starts_at`, `ends_at`. `outside_working_hours`: `weekday` (no hours that day) or `hours` (the day's `start`/`end` pairs). `not_employed`: `status`, `started_at`, `ended_at`.","additionalProperties":true}},"additionalProperties":false},"ScheduleBoardFinding":{"description":"A finding on the board, with the job and employee it concerns.","allOf":[{"$ref":"#/components/schemas/ScheduleFinding"},{"type":"object","required":["work_order_id","order_number","employee_id"],"properties":{"work_order_id":{"type":"string","format":"uuid","description":"The job checked."},"order_number":{"type":"string","description":"Its number."},"employee_id":{"type":"string","format":"uuid","description":"The employee checked."}},"additionalProperties":false}]},"ScheduleConflictError":{"type":"object","description":"An assignment or move refused because it would clash. Nothing was written.","required":["message","error","conflicts"],"properties":{"message":{"type":"string","description":"\"That would clash.\""},"error":{"type":"string","enum":["schedule_conflict"],"description":"Stable error code."},"conflicts":{"type":"array","description":"Every finding, errors and warnings both.","items":{"$ref":"#/components/schemas/ScheduleFinding"}}},"additionalProperties":false},"BookingConflictError":{"type":"object","description":"The request is valid but the record's state refuses it — a status transition the job does not allow, a change to a finished job, a duplicate. `message` says which; nothing was changed.","required":["message","error"],"properties":{"message":{"type":"string","description":"What was refused and why, in English."},"error":{"type":"string","description":"Stable error code: `conflict`."}},"additionalProperties":false},"BookingDeleted":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"boolean","enum":[true]}},"additionalProperties":false},"BookingCursorPage":{"type":"object","description":"A page of a cursor-paginated list. Pass `next_cursor` as `cursor` to fetch the next page; it is null on the last page.","required":["data","path","per_page","next_cursor","next_page_url","prev_cursor","prev_page_url"],"properties":{"data":{"type":"array"},"path":{"type":"string","description":"The list URL without query."},"per_page":{"type":"integer","description":"Items per page."},"next_cursor":{"type":["string","null"],"description":"Cursor for the next page, or null."},"next_page_url":{"type":["string","null"],"description":"URL of the next page, or null."},"prev_cursor":{"type":["string","null"],"description":"Cursor for the previous page, or null."},"prev_page_url":{"type":["string","null"],"description":"URL of the previous page, or null."}},"additionalProperties":false}},"responses":{"Unauthorized":{"description":"No key, or the key is invalid, revoked or expired, or its creator no longer has access.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"message":"Invalid, revoked or expired API key.","error":"invalid_api_key"}}}},"Forbidden":{"description":"The key lacks the scope, or the member it acts as lacks the permission or holds no seat in the module (`module_seat_required` — an administrator assigns seats under Settings → Users), or the organization is suspended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"message":"This API key is read-only. Create a key with the \"write\" scope to change data.","error":"insufficient_scope","required":"write"}}}},"PaymentRequired":{"description":"The organization has not bought the module this endpoint belongs to, or it has lapsed to read-only, or its tier does not include what the operation needs: `feature` names the capability (the operation's `x-feature`) and `required_tier` the cheapest tier that grants it. Every operation answers this with `\"feature\": \"core.api\"` when no module the organization holds is on a tier that includes the API.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModuleLockedError"},"example":{"message":"The booking module is not active for this organization.","error":"module_locked","module":"booking","feature":null,"current_tier":null,"required_tier":null}}}},"NotFound":{"description":"No such record in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"message":"Resource not found.","error":"not_found"}}}},"ValidationFailed":{"description":"The request body or query failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"message":"The title field is required.","error":"validation_failed","errors":{"title":["The title field is required."]}}}}},"RateLimited":{"description":"The key has made more requests this minute than its limit allows.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"message":"Rate limit exceeded.","error":"rate_limited","retry_after":42}}}},"ArticleNumberTaken":{"description":"Another article — possibly a deleted one — already has this article number. `error` is `conflict`; the message names the article that holds the number.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"message":"Article number ST-100 already belongs to \"Hemstädning\", which was deleted but still holds the number.","error":"conflict"}}}},"DocumentConflict":{"description":"The document is not in a state that allows this. `error` says which: `document_not_sent` — it has not been sent yet; `recipient_not_on_document` — the recipient is not on its current signature request; `recipient_already_signed` — the recipient has already signed and cannot be removed. `message` says the same for a person.","content":{"application/json":{"schema":{"type":"object","required":["message","error"],"properties":{"message":{"type":"string","description":"What was refused and why, for a person."},"error":{"type":"string","enum":["document_not_sent","recipient_not_on_document","recipient_already_signed","conflict"],"description":"Stable error code. `conflict` is the generic code for a refusal without a more specific one."}},"additionalProperties":false},"example":{"message":"This document has not been sent yet.","error":"document_not_sent"}}}},"BankIdUnavailable":{"description":"BankID was asked for — on the document, or for a recipient — and this server has no working BankID relying-party certificate. Nothing was changed or sent. `bankid_unavailable` is the only `error` here; it is not something a retry fixes.","content":{"application/json":{"schema":{"type":"object","required":["message","error"],"properties":{"message":{"type":"string"},"error":{"type":"string","enum":["bankid_unavailable"]}},"additionalProperties":false},"example":{"message":"BankID is not available on this server.","error":"bankid_unavailable"}}}},"InvoiceConflict":{"description":"The request is well-formed but the invoice's state does not allow it — for example it has already been issued, has no lines, or has been credited in full. `error` is always `conflict`; the message says what to do instead.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"message":"Invoice A-1042 is sent and can no longer be edited. Correct it with a credit note.","error":"conflict"}}}},"QuoteConflict":{"description":"The quote's state does not allow this. `error` says which: `quote_not_draft` — it has been sent; revert it to draft before changing it; `quote_locked` — it has been accepted or rejected and can no longer change; `quote_expired` — its last valid day has passed; `quote_not_sent`, `quote_already_accepted`, `quote_rejected` — it is not open for the buyer's answer; `quote_not_accepted` — only an accepted quote can be handed over; `handover_unavailable` — the organization has neither the scheduling nor the invoicing module, so an accepted quote has nothing to become. `message` says the same for a person.","content":{"application/json":{"schema":{"type":"object","required":["message","error"],"properties":{"message":{"type":"string","description":"What was refused and why, for a person."},"error":{"type":"string","enum":["quote_not_draft","quote_locked","quote_expired","quote_not_sent","quote_already_accepted","quote_rejected","quote_not_accepted","handover_unavailable"],"description":"Stable error code."}},"additionalProperties":false},"example":{"message":"Quote Q-00042 has been sent. Revert it to draft before changing it — that withdraws the link the buyer has.","error":"quote_not_draft"}}}},"WebhookForbidden":{"description":"The key lacks the `admin` scope (`insufficient_scope`), or the member it acts as lacks `webhooks.manage` (`permission_denied`), or the organization is suspended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"message":"This API key needs the \"admin\" scope for this endpoint.","error":"insufficient_scope","required":"admin"}}}},"WebhookValidationFailed":{"description":"The body failed validation — for example an event name Closepact does not send, or a URL that is not http(s).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"},"example":{"message":"Unknown event: deal.create. Subscribe to any of deal.created, deal.stage_changed, deal.won, deal.lost, room.created, room.published, stakeholder.added, message.posted, quote.accepted, signature.requested, document.viewed, signer.signed, signature.completed, signature.canceled, work_order.completed, invoice.issued, work_order.created_from_quote, invoice.created_from_quote, or \"*\" for every event.","error":"validation_failed","errors":{"events":["Unknown event: deal.create. Subscribe to any of deal.created, deal.stage_changed, deal.won, deal.lost, room.created, room.published, stakeholder.added, message.posted, quote.accepted, signature.requested, document.viewed, signer.signed, signature.completed, signature.canceled, work_order.completed, invoice.issued, work_order.created_from_quote, invoice.created_from_quote, or \"*\" for every event."]}}}}},"WebhookAcknowledged":{"description":"Answer with any 2xx status within 15 seconds to acknowledge the event; the body is ignored. Anything else — another status, a redirect, a timeout or a refused connection — is retried, up to 7 attempts in all."}}}}