{"openapi":"3.1.0","info":{"title":"Watchdog API","version":"0.0.0","description":"Watchdog public API. Server-to-server document upload and related operations.\n\n## Authentication\n\nProtected public endpoints require an API key in the `Authorization` header (for example, `Authorization: Bearer wdg_<your-token>`).\n\n**Generate a token:** sign in at [app.watchdog.no](https://app.watchdog.no), open **Settings → Personal → API keys**, and click **Create API key**. The plaintext token is shown **once** at creation and cannot be recovered — copy it into a secrets manager immediately. If lost, revoke and replace.\n\nChoose Read, Write or Admin. Write includes Read; Admin includes Write. Effective access is limited by the owner’s current organization role. Send `X-Organization-Id` with every personal-key request. Migrated keys may omit it to use their original organization. Required levels are listed in `x-watchdog-access.permission`."},"servers":[{"url":"https://api.watchdog.no","description":"Production"}],"components":{"securitySchemes":{"ApiKeyBearer":{"type":"http","scheme":"bearer","description":"Personal API key from Settings → Personal → API keys. Send X-Organization-Id with the target organization. Plaintext is shown once at creation and cannot be recovered. Header: `Authorization: Bearer wdg_<token>`."}},"schemas":{"Health":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"commit":{"type":"string"}},"required":["status","commit"]},"UploadInvoiceResponse":{"type":"object","properties":{"invoice_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued"]},"documents":{"type":"object","properties":{"primary":{"$ref":"#/components/schemas/UploadInvoiceDocumentRef"},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/UploadInvoiceDocumentRef"}}},"required":["primary","attachments"]},"created_at":{"type":"string","format":"date-time"}},"required":["invoice_id","status","documents","created_at"]},"UploadInvoiceDocumentRef":{"type":"object","properties":{"document_id":{"type":"string","format":"uuid"},"file_name":{"type":"string"},"mime_type":{"type":"string","enum":["application/pdf","application/xml","text/xml"]}},"required":["document_id","file_name","mime_type"]},"ErrorBody_invalid_request":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_request"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_too_many_attachments":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["too_many_attachments"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_per_file_validation":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_xml_invoice","unnamed_file","unsupported_media_type","file_too_small","payload_too_large"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"},"field":{"type":"string"},"filename":{"type":"string"}},"required":["code","message","request_id","field","filename"]}},"required":["error"]},"ErrorBody_invalid_token":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_token"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_token_disabled":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["token_disabled"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_insufficient_scope":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["insufficient_scope"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_organization_required":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["organization_required"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_insufficient_role":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["insufficient_role"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_invoice_processing_capacity_exhausted":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["invoice_processing_capacity_exhausted"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_duplicate_invoice":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["duplicate_invoice"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"},"existing_invoice_id":{"type":"string","format":"uuid"}},"required":["code","message","request_id","existing_invoice_id"]}},"required":["error"]},"ErrorBody_primary_md5_locked_by_deleted_invoice":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["primary_md5_locked_by_deleted_invoice"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"},"existing_invoice_id":{"type":"string","format":"uuid"}},"required":["code","message","request_id","existing_invoice_id"]}},"required":["error"]},"ErrorBody_length_required":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["length_required"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_payload_too_large":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["payload_too_large"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_unsupported_media_type":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["unsupported_media_type"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_rate_limit_exceeded":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["rate_limit_exceeded"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_internal_error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["internal_error"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_service_unavailable":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["service_unavailable"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"UploadInvoiceRequest":{"type":"object","required":["primary"],"properties":{"primary":{"type":"string","format":"binary","description":"Primary invoice document. Max 50 MB."},"attachments":{"type":"array","items":{"type":"string","format":"binary"},"description":"Optional supporting documents (0–9). Max 25 MB each. Combined body (primary + attachments) max 100 MB."}}},"InvoiceListResponse":{"type":"object","properties":{"invoices":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceStatusResponse"}},"next_cursor":{"type":["string","null"],"description":"Opaque cursor for the next page, or `null` when no more invoices remain."}},"required":["invoices","next_cursor"]},"InvoiceStatusResponse":{"type":"object","properties":{"invoice_id":{"type":"string","format":"uuid","description":"Watchdog invoice identifier returned by the upload endpoint.","example":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb"},"invoice_number":{"type":["string","null"],"description":"Invoice number extracted from the document. For a semantic duplicate, this is the canonical invoice number. May be `null` before extraction or when no number can be identified; it is not a unique identifier.","example":"100234"},"duplicate_of_invoice_id":{"type":["string","null"],"format":"uuid","description":"Canonical invoice identifier when this accepted upload was quarantined as a semantic duplicate; otherwise null.","example":null},"processing":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["pending","processing"]},"classification":{"type":"null"},"failure":{"type":"null"}},"required":["status","classification","failure"]},{"type":"object","properties":{"status":{"type":"string","enum":["completed"]},"classification":{"type":"string","enum":["invoice","credit_note","self_billed_invoice","not_invoice"]},"failure":{"type":"null"}},"required":["status","classification","failure"]},{"type":"object","properties":{"status":{"type":"string","enum":["failed"]},"classification":{"type":"null"},"failure":{"type":"object","properties":{"code":{"type":"string","enum":["invoice_processing_failed"]},"message":{"type":"string","enum":["Watchdog could not process this document."]}},"required":["code","message"]}},"required":["status","classification","failure"]}]},"alerts":{"anyOf":[{"type":"object","properties":{"processing":{"type":"object","properties":{"status":{"type":"string","enum":["pending","processing"]},"skip_reason":{"type":"null"},"failure":{"type":"null"}},"required":["status","skip_reason","failure"]},"result":{"type":"null"},"count":{"type":"null"},"resolution":{"type":"null"}},"required":["processing","result","count","resolution"]},{"type":"object","properties":{"processing":{"type":"object","properties":{"status":{"type":"string","enum":["pending","processing"]},"skip_reason":{"type":"null"},"failure":{"type":"null"}},"required":["status","skip_reason","failure"]},"result":{"type":"string","enum":["alerts_found"]},"count":{"type":"integer","exclusiveMinimum":0},"resolution":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["open"]},"method":{"type":"null"}},"required":["status","method"]},{"type":"object","properties":{"status":{"type":"string","enum":["resolved"]},"method":{"type":"string","enum":["credit_note"]}},"required":["status","method"]},{"type":"object","properties":{"status":{"type":"string","enum":["resolved"]},"method":{"type":"string","enum":["manual_review"]}},"required":["status","method"]}]}},"required":["processing","result","count","resolution"]},{"type":"object","properties":{"processing":{"type":"object","properties":{"status":{"type":"string","enum":["completed"]},"skip_reason":{"type":"null"},"failure":{"type":"null"}},"required":["status","skip_reason","failure"]},"result":{"type":"string","enum":["no_alerts_found"]},"count":{"type":"number","enum":[0]},"resolution":{"type":"null"}},"required":["processing","result","count","resolution"]},{"type":"object","properties":{"processing":{"type":"object","properties":{"status":{"type":"string","enum":["completed"]},"skip_reason":{"type":"null"},"failure":{"type":"null"}},"required":["status","skip_reason","failure"]},"result":{"type":"string","enum":["alerts_found"]},"count":{"type":"integer","exclusiveMinimum":0},"resolution":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["open"]},"method":{"type":"null"}},"required":["status","method"]},{"type":"object","properties":{"status":{"type":"string","enum":["resolved"]},"method":{"type":"string","enum":["credit_note"]}},"required":["status","method"]},{"type":"object","properties":{"status":{"type":"string","enum":["resolved"]},"method":{"type":"string","enum":["manual_review"]}},"required":["status","method"]}]}},"required":["processing","result","count","resolution"]},{"type":"object","properties":{"processing":{"type":"object","properties":{"status":{"type":"string","enum":["failed"]},"skip_reason":{"type":"null"},"failure":{"type":"object","properties":{"code":{"type":"string","enum":["some_alert_checks_failed"]},"message":{"type":"string","enum":["Some agreement checks could not be completed."]}},"required":["code","message"]}},"required":["status","skip_reason","failure"]},"result":{"type":"null"},"count":{"type":"null"},"resolution":{"type":"null"}},"required":["processing","result","count","resolution"]},{"type":"object","properties":{"processing":{"type":"object","properties":{"status":{"type":"string","enum":["failed"]},"skip_reason":{"type":"null"},"failure":{"type":"object","properties":{"code":{"type":"string","enum":["some_alert_checks_failed"]},"message":{"type":"string","enum":["Some agreement checks could not be completed."]}},"required":["code","message"]}},"required":["status","skip_reason","failure"]},"result":{"type":"string","enum":["alerts_found"]},"count":{"type":"integer","exclusiveMinimum":0},"resolution":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["open"]},"method":{"type":"null"}},"required":["status","method"]},{"type":"object","properties":{"status":{"type":"string","enum":["resolved"]},"method":{"type":"string","enum":["credit_note"]}},"required":["status","method"]},{"type":"object","properties":{"status":{"type":"string","enum":["resolved"]},"method":{"type":"string","enum":["manual_review"]}},"required":["status","method"]}]}},"required":["processing","result","count","resolution"]},{"type":"object","properties":{"processing":{"type":"object","properties":{"status":{"type":"string","enum":["skipped"]},"skip_reason":{"type":"string","enum":["not_an_invoice","invoice_processing_failed","credit_note","no_applicable_agreements","credit_note_matched","duplicate_invoice"]},"failure":{"type":"null"}},"required":["status","skip_reason","failure"]},"result":{"type":"null"},"count":{"type":"null"},"resolution":{"type":"null"}},"required":["processing","result","count","resolution"]}],"description":"Aggregate alert-assessment summary across all applicable agreements. `count` preserves detected, non-superseded alerts even after review; `resolution` indicates whether they remain blocking. Individual alerts and agreement-level results are not exposed; use `invoice_url` to inspect them in Watchdog."},"invoice_url":{"type":"string","format":"uri","description":"Absolute URL for reviewing the invoice and its alerts in Watchdog. For a semantic duplicate, this opens the canonical invoice.","example":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb"},"created_at":{"type":"string","format":"date-time","description":"When the invoice resource was created.","example":"2026-08-12T07:44:20.000Z"}},"required":["invoice_id","invoice_number","duplicate_of_invoice_id","processing","alerts","invoice_url","created_at"]},"ErrorBody_validation_error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["validation_error"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_mfa_required":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["mfa_required"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]},"ErrorBody_not_found":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["not_found"]},"message":{"type":"string"},"description":{"type":"string"},"request_id":{"type":"string"}},"required":["code","message","request_id"]}},"required":["error"]}},"parameters":{}},"paths":{"/health":{"get":{"operationId":"getHealth","tags":["operations"],"summary":"Health check","description":"Returns 200 when the API process is up. No dependency checks.","parameters":[],"security":[],"x-hidden":false,"x-watchdog-access":{"public":true,"authenticated":false},"responses":{"200":{"description":"Service is up","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/v1/invoices/upload":{"post":{"operationId":"uploadInvoice","tags":["invoices"],"summary":"Upload an invoice","description":"Submit a `primary` invoice document and optional `attachments` as `multipart/form-data`.\n\nFiles (primary and attachments) must be PDF, **Peppol BIS Billing 3.0** (UBL 2.1 — root `Invoice` or `CreditNote`), or **Finvoice 3.0** (root `Finvoice`). XML is matched on the root element name only; documents are not schema-validated. If any file fails validation, no invoice is created.\n\nDeduplicated by primary-document MD5 within the organization. A replay returns 409 `duplicate_invoice`, or `primary_md5_locked_by_deleted_invoice` if the original was soft-deleted (an admin must hard-delete to free the MD5).\n\nA different rendering of the same invoice is accepted normally, then checked semantically during processing. Polling may complete with `duplicate_of_invoice_id` and skipped alert processing; the retained upload remains addressable by its own `invoice_id`.\n\nNew invoices also require current processing capacity. A 403 `invoice_processing_capacity_exhausted` means no invoice or documents were retained; retry only after organization capacity may have changed. A temporary admission-coordination failure returns 503 `service_unavailable` with `Retry-After: 1`.\n\nThe 201 includes a `Location` header at `/v1/invoices/{invoice_id}`. Poll that resource with a Read key and respect its `Retry-After` header while either processing status is `pending` or `processing`.","parameters":[{"name":"X-Organization-Id","in":"header","required":false,"schema":{"type":"string"},"description":"Required for personal API keys. Target one organization you have access to. Migrated keys may omit it to use their original organization. For Clerk sessions, it must match the active organization."}],"security":[{"ApiKeyBearer":[]}],"x-hidden":false,"x-watchdog-access":{"public":true,"authenticated":true,"permission":"write"},"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/UploadInvoiceRequest"}}}},"responses":{"201":{"description":"Invoice created and queued for processing.","headers":{"Location":{"schema":{"type":"string"},"description":"URL of the pollable invoice resource (`/v1/invoices/{invoice_id}`)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadInvoiceResponse"}}}},"400":{"description":"Invalid multipart body, or a per-file validation failed. Per-file errors include `field` and `filename` so the client can pinpoint the offending file.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorBody_invalid_request"},{"$ref":"#/components/schemas/ErrorBody_too_many_attachments"},{"$ref":"#/components/schemas/ErrorBody_per_file_validation"}]}}}},"401":{"description":"Authentication failed — missing, malformed, unknown, or revoked token. See `WWW-Authenticate` header per RFC 6750.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_invalid_token"}}}},"403":{"description":"The credential is disabled or lacks the required permission, the Clerk session has no supported active organization role, or current organization capacity forbids accepting another invoice. `invoice_processing_capacity_exhausted` retains no invoice or documents; retry only after organization capacity may have changed.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorBody_token_disabled"},{"$ref":"#/components/schemas/ErrorBody_insufficient_scope"},{"$ref":"#/components/schemas/ErrorBody_organization_required"},{"$ref":"#/components/schemas/ErrorBody_insufficient_role"},{"$ref":"#/components/schemas/ErrorBody_invoice_processing_capacity_exhausted"}]}}}},"409":{"description":"The primary document duplicates an existing or soft-deleted invoice. Both codes include `existing_invoice_id`.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorBody_duplicate_invoice"},{"$ref":"#/components/schemas/ErrorBody_primary_md5_locked_by_deleted_invoice"}]}}}},"411":{"description":"`Content-Length` header is required for this endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_length_required"}}}},"413":{"description":"Request body exceeds the per-file or combined-body cap.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorBody_payload_too_large"},{"$ref":"#/components/schemas/ErrorBody_per_file_validation"}]}}}},"415":{"description":"Unsupported MIME type, or the primary file failed magic-byte validation.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorBody_unsupported_media_type"},{"$ref":"#/components/schemas/ErrorBody_per_file_validation"}]}}}},"429":{"description":"Rate limit exceeded. Pre-auth IP cap is 300 req/min; per-credential cap is 120 req/min. Response headers indicate when the caller may retry.","headers":{"Retry-After":{"description":"Seconds until the caller may retry.","schema":{"type":"string","example":"34"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"string","example":"300"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"string","example":"0"}},"X-RateLimit-Reset":{"description":"Unix timestamp in seconds when the current window resets. Divide by 1000 if you have a millisecond-precision clock.","schema":{"type":"string","example":"1713793234"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_rate_limit_exceeded"}}}},"500":{"description":"Internal error processing the upload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_internal_error"}}}},"503":{"description":"Authentication or invoice admission coordination is temporarily unavailable. The same request may be retried shortly.","headers":{"Retry-After":{"schema":{"type":"string","example":"1"},"description":"Retry delay in seconds for temporary coordination or identity-provider throttling."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_service_unavailable"}}}}}}},"/v1/invoices":{"get":{"operationId":"listInvoices","tags":["invoices"],"summary":"List invoices","description":"Lists every non-deleted invoice belonging to the token organization, regardless of import source.\n\nThis operational listing includes accepted uploads quarantined as semantic duplicates. Those resources expose `duplicate_of_invoice_id` and do not represent additional financial invoices.\n\nResults default to newest first. Use `sort` and `direction` to choose an allowlisted order. Pass `next_cursor` as `cursor` to continue; omitted sorting parameters are inherited from the cursor, while contradictory parameters return `400`.\n\nCursors are opaque and remain usable if the invoice at the preceding page boundary is deleted.\n\nEach item is the same invoice resource returned by `GET /v1/invoices/{invoice_id}`. Open `invoice_url` in Watchdog for details and alert review.","parameters":[{"name":"X-Organization-Id","in":"header","required":false,"schema":{"type":"string"},"description":"Required for personal API keys. Target one organization you have access to. Migrated keys may omit it to use their original organization. For Clerk sessions, it must match the active organization."},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Maximum invoices to return. Defaults to 50 and cannot exceed 100.","example":50},"required":false,"description":"Maximum invoices to return. Defaults to 50 and cannot exceed 100.","name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Opaque continuation cursor from the preceding response. It retains the selected sort and direction.","example":"eyJ2IjoxLCJyZXNvdXJjZSI6Imludm9pY2VzIiwic29ydCI6ImNyZWF0ZWRfYXQiLCJkaXJlY3Rpb24iOiJkZXNjIiwic29ydF92YWx1ZSI6IjE3ODY0NjM5NjAwNjEwMDAiLCJpZCI6IjdhZDJjNTBkLTJjYzAtNDlhMC05YTljLTZmYTgxN2MxZmJjYiJ9"},"required":false,"description":"Opaque continuation cursor from the preceding response. It retains the selected sort and direction.","name":"cursor","in":"query"},{"schema":{"type":"string","enum":["created_at"],"default":"created_at","description":"Field used to order invoices. Defaults to `created_at` for an initial request and is inherited from the cursor on continuation.","example":"created_at"},"required":false,"description":"Field used to order invoices. Defaults to `created_at` for an initial request and is inherited from the cursor on continuation.","name":"sort","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc","description":"Sort direction. Defaults to `desc` for an initial request and is inherited from the cursor on continuation.","example":"desc"},"required":false,"description":"Sort direction. Defaults to `desc` for an initial request and is inherited from the cursor on continuation.","name":"direction","in":"query"}],"security":[{"ApiKeyBearer":[]}],"x-hidden":false,"x-watchdog-access":{"public":true,"authenticated":true,"permission":"read"},"responses":{"200":{"description":"A page of invoices ordered by the selected field and invoice ID.","headers":{"Cache-Control":{"schema":{"type":"string","example":"no-store"},"description":"Prevents changing invoice states from being cached."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceListResponse"},"example":{"invoices":[{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":"100234","duplicate_of_invoice_id":null,"processing":{"status":"completed","classification":"invoice","failure":null},"alerts":{"processing":{"status":"completed","skip_reason":null,"failure":null},"result":"no_alerts_found","count":0,"resolution":null},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}],"next_cursor":null}}}},"400":{"description":"The limit, sort, direction, or cursor is invalid.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorBody_validation_error"},{"$ref":"#/components/schemas/ErrorBody_invalid_request"}]}}}},"401":{"description":"Authentication failed — missing, malformed, unknown, or revoked token. See `WWW-Authenticate` header per RFC 6750.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_invalid_token"}}}},"403":{"description":"The credential is disabled, lacks the required permission, has no active organization, carries an unsupported organization role, or does not satisfy the organization MFA policy.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorBody_token_disabled"},{"$ref":"#/components/schemas/ErrorBody_insufficient_scope"},{"$ref":"#/components/schemas/ErrorBody_organization_required"},{"$ref":"#/components/schemas/ErrorBody_insufficient_role"},{"$ref":"#/components/schemas/ErrorBody_mfa_required"}]}}}},"429":{"description":"Rate limit exceeded. Pre-auth IP cap is 300 req/min; per-credential cap is 120 req/min. Response headers indicate when the caller may retry.","headers":{"Retry-After":{"description":"Seconds until the caller may retry.","schema":{"type":"string","example":"34"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"string","example":"300"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"string","example":"0"}},"X-RateLimit-Reset":{"description":"Unix timestamp in seconds when the current window resets. Divide by 1000 if you have a millisecond-precision clock.","schema":{"type":"string","example":"1713793234"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_rate_limit_exceeded"}}}},"500":{"description":"Internal error listing invoices.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_internal_error"}}}},"503":{"description":"Authentication infrastructure is unavailable or rate limited. Honor Retry-After when provided.","headers":{"Retry-After":{"description":"Seconds until the caller may retry.","schema":{"type":"string","example":"34"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_service_unavailable"}}}}}}},"/v1/invoices/{invoice_id}":{"get":{"operationId":"getInvoice","tags":["invoices"],"summary":"Get invoice","description":"Poll the invoice resource returned in the upload `Location` header or referenced by `existing_invoice_id` in a duplicate response.\n\nThe `processing` and `alerts.processing` objects have independent lifecycles. Continue polling while either status is `pending` or `processing`, and respect `Retry-After` before polling again.\n\n`alerts` is an aggregate across applicable agreements: active agreements with a confirmed or auto-confirmed invoice match. An applicable agreement that cannot be checked makes the aggregate fail; only an invoice without such agreements uses `no_applicable_agreements`.\n\n`count` contains detected, non-superseded alerts and may increase while assessment is active. Dismissed and credited alerts remain in the detected count, while `resolution` indicates whether the findings remain blocking: `manual_review` means every current alert is dismissed or credited and the invoice has no qualifying full credit note; `credit_note` means the invoice has a uniquely matched credit note that offsets its total including VAT within ±1 currency unit in the same currency. A null count during active processing does not mean that no alerts were found. Individual alerts and agreement-level results remain available through `invoice_url`.\n\n`alerts.result` and `alerts.resolution` must be interpreted together with `alerts.processing.status`. Neither is by itself an approval signal while alert processing is `pending`, `processing`, or `failed`: active assessment may find more alerts, and failed assessment is incomplete. `completed` is conclusive for the current snapshot; `skipped` means assessment did not run and is explained by `skip_reason`.\n\nThis response is a current snapshot, not an immutable assessment event. A terminal response may change after agreement edits, rechecks, alert review, replacements, or credit-note matching. Consumers that need later changes must fetch the resource again on their own cadence.\n\n`invoice_id` is the stable API identifier. `invoice_number` is extracted metadata and must not be treated as unique.\n\nA semantic duplicate remains a terminal, pollable upload resource: `duplicate_of_invoice_id` identifies the canonical invoice and alert processing is skipped with `duplicate_invoice`.","parameters":[{"name":"X-Organization-Id","in":"header","required":false,"schema":{"type":"string"},"description":"Required for personal API keys. Target one organization you have access to. Migrated keys may omit it to use their original organization. For Clerk sessions, it must match the active organization."},{"schema":{"type":"string","format":"uuid","description":"Invoice identifier from a successful upload or duplicate response.","example":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb"},"required":true,"description":"Invoice identifier from a successful upload or duplicate response.","name":"invoice_id","in":"path"}],"security":[{"ApiKeyBearer":[]}],"x-hidden":false,"x-watchdog-access":{"public":true,"authenticated":true,"permission":"read"},"responses":{"200":{"description":"Current invoice processing and aggregate alert-assessment state.","headers":{"Cache-Control":{"schema":{"type":"string","example":"no-store"},"description":"Prevents a changing polling response from being cached."},"Retry-After":{"schema":{"type":"string","example":"10"},"description":"Seconds before polling again. Present exactly while either processing status is `pending` or `processing`."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceStatusResponse"},"examples":{"processing":{"summary":"Invoice processing is underway","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":null,"duplicate_of_invoice_id":null,"processing":{"status":"processing","classification":null,"failure":null},"alerts":{"processing":{"status":"pending","skip_reason":null,"failure":null},"result":null,"count":null,"resolution":null},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}},"assessmentProcessing":{"summary":"Assessment is underway with alerts detected so far","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":"100234","duplicate_of_invoice_id":null,"processing":{"status":"completed","classification":"invoice","failure":null},"alerts":{"processing":{"status":"processing","skip_reason":null,"failure":null},"result":"alerts_found","count":2,"resolution":{"status":"open","method":null}},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}},"alertsFound":{"summary":"Assessment completed with alerts","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":"100234","duplicate_of_invoice_id":null,"processing":{"status":"completed","classification":"invoice","failure":null},"alerts":{"processing":{"status":"completed","skip_reason":null,"failure":null},"result":"alerts_found","count":7,"resolution":{"status":"open","method":null}},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}},"noAlerts":{"summary":"Assessment completed without alerts","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":"100234","duplicate_of_invoice_id":null,"processing":{"status":"completed","classification":"invoice","failure":null},"alerts":{"processing":{"status":"completed","skip_reason":null,"failure":null},"result":"no_alerts_found","count":0,"resolution":null},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}},"partialFailure":{"summary":"Some checks failed after other checks found alerts","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":"100234","duplicate_of_invoice_id":null,"processing":{"status":"completed","classification":"invoice","failure":null},"alerts":{"processing":{"status":"failed","skip_reason":null,"failure":{"code":"some_alert_checks_failed","message":"Some agreement checks could not be completed."}},"result":"alerts_found","count":2,"resolution":{"status":"open","method":null}},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}},"notInvoice":{"summary":"Document was not an invoice","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":null,"duplicate_of_invoice_id":null,"processing":{"status":"completed","classification":"not_invoice","failure":null},"alerts":{"processing":{"status":"skipped","skip_reason":"not_an_invoice","failure":null},"result":null,"count":null,"resolution":null},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}},"noApplicableAgreements":{"summary":"No applicable agreements","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":"100234","duplicate_of_invoice_id":null,"processing":{"status":"completed","classification":"invoice","failure":null},"alerts":{"processing":{"status":"skipped","skip_reason":"no_applicable_agreements","failure":null},"result":null,"count":null,"resolution":null},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}},"creditNote":{"summary":"Document is a credit note","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":"CN-100234","duplicate_of_invoice_id":null,"processing":{"status":"completed","classification":"credit_note","failure":null},"alerts":{"processing":{"status":"skipped","skip_reason":"credit_note","failure":null},"result":null,"count":null,"resolution":null},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}},"creditNoteMatched":{"summary":"Assessment was pre-empted by a matched credit note","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":"100234","duplicate_of_invoice_id":null,"processing":{"status":"completed","classification":"invoice","failure":null},"alerts":{"processing":{"status":"skipped","skip_reason":"credit_note_matched","failure":null},"result":null,"count":null,"resolution":null},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}},"semanticDuplicate":{"summary":"Upload was quarantined as a semantic duplicate","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":"100234","duplicate_of_invoice_id":"39a5e843-1c84-4210-82cb-e577c8b83799","processing":{"status":"completed","classification":"invoice","failure":null},"alerts":{"processing":{"status":"skipped","skip_reason":"duplicate_invoice","failure":null},"result":null,"count":null,"resolution":null},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/39a5e843-1c84-4210-82cb-e577c8b83799","created_at":"2026-08-12T07:44:20.000Z"}},"invoiceFailure":{"summary":"Invoice processing failed","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":null,"duplicate_of_invoice_id":null,"processing":{"status":"failed","classification":null,"failure":{"code":"invoice_processing_failed","message":"Watchdog could not process this document."}},"alerts":{"processing":{"status":"skipped","skip_reason":"invoice_processing_failed","failure":null},"result":null,"count":null,"resolution":null},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}},"creditNoteResolution":{"summary":"Detected alerts were resolved by a credit note","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":"100234","duplicate_of_invoice_id":null,"processing":{"status":"completed","classification":"invoice","failure":null},"alerts":{"processing":{"status":"completed","skip_reason":null,"failure":null},"result":"alerts_found","count":7,"resolution":{"status":"resolved","method":"credit_note"}},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}},"manualReviewResolution":{"summary":"Detected alerts were resolved through manual review","value":{"invoice_id":"7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","invoice_number":"100234","duplicate_of_invoice_id":null,"processing":{"status":"completed","classification":"invoice","failure":null},"alerts":{"processing":{"status":"completed","skip_reason":null,"failure":null},"result":"alerts_found","count":2,"resolution":{"status":"resolved","method":"manual_review"}},"invoice_url":"https://app.watchdog.no/org_3AFsEYath1408oAK0EkJikS8qp1/invoices/7ad2c50d-2cc0-49a0-9a9c-6fa817c1fbcb","created_at":"2026-08-12T07:44:20.000Z"}}}}}},"400":{"description":"The invoice ID is not a valid UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_validation_error"}}}},"401":{"description":"Authentication failed — missing, malformed, unknown, or revoked token. See `WWW-Authenticate` header per RFC 6750.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_invalid_token"}}}},"403":{"description":"The credential is disabled, lacks the required permission, has no active organization, carries an unsupported organization role, or does not satisfy the organization MFA policy.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorBody_token_disabled"},{"$ref":"#/components/schemas/ErrorBody_insufficient_scope"},{"$ref":"#/components/schemas/ErrorBody_organization_required"},{"$ref":"#/components/schemas/ErrorBody_insufficient_role"},{"$ref":"#/components/schemas/ErrorBody_mfa_required"}]}}}},"404":{"description":"Invoice not found. Missing, deleted, and cross-organization IDs are identical.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_not_found"}}}},"429":{"description":"Rate limit exceeded. Pre-auth IP cap is 300 req/min; per-credential cap is 120 req/min. Response headers indicate when the caller may retry.","headers":{"Retry-After":{"description":"Seconds until the caller may retry.","schema":{"type":"string","example":"34"}},"X-RateLimit-Limit":{"description":"Maximum requests allowed in the current window.","schema":{"type":"string","example":"300"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"string","example":"0"}},"X-RateLimit-Reset":{"description":"Unix timestamp in seconds when the current window resets. Divide by 1000 if you have a millisecond-precision clock.","schema":{"type":"string","example":"1713793234"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_rate_limit_exceeded"}}}},"500":{"description":"Internal error reading invoice status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_internal_error"}}}},"503":{"description":"Authentication infrastructure is unavailable or rate limited. Honor Retry-After when provided.","headers":{"Retry-After":{"description":"Seconds until the caller may retry.","schema":{"type":"string","example":"34"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody_service_unavailable"}}}}}}}},"webhooks":{}}