curl --request POST \
--url https://api.watchdog.no/v1/invoices/upload \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form primary='@example-file' \
--form attachments.items='@example-file'const form = new FormData();
form.append('primary', '<string>');
form.append('attachments.items', '{
"fileName": "example-file"
}');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://api.watchdog.no/v1/invoices/upload', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.watchdog.no/v1/invoices/upload"
files = {
"primary": ("example-file", open("example-file", "rb")),
"attachments.items": ("example-file", open("example-file", "rb"))
}
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, files=files, headers=headers)
print(response.text){
"invoice_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "queued",
"documents": {
"primary": {
"document_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"file_name": "<string>",
"mime_type": "application/pdf"
},
"attachments": [
{
"document_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"file_name": "<string>",
"mime_type": "application/pdf"
}
]
},
"created_at": "2023-11-07T05:31:56Z"
}{
"error": {
"code": "invalid_request",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "invalid_token",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "token_disabled",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "duplicate_invoice",
"message": "<string>",
"request_id": "<string>",
"existing_invoice_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"description": "<string>"
}
}{
"error": {
"code": "length_required",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "payload_too_large",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "unsupported_media_type",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "rate_limit_exceeded",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "internal_error",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "service_unavailable",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}Upload an invoice
Submit a primary invoice document and optional attachments as multipart/form-data.
Files (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.
Deduplicated 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).
A 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.
New 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.
The 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.
curl --request POST \
--url https://api.watchdog.no/v1/invoices/upload \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form primary='@example-file' \
--form attachments.items='@example-file'const form = new FormData();
form.append('primary', '<string>');
form.append('attachments.items', '{
"fileName": "example-file"
}');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://api.watchdog.no/v1/invoices/upload', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.watchdog.no/v1/invoices/upload"
files = {
"primary": ("example-file", open("example-file", "rb")),
"attachments.items": ("example-file", open("example-file", "rb"))
}
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, files=files, headers=headers)
print(response.text){
"invoice_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "queued",
"documents": {
"primary": {
"document_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"file_name": "<string>",
"mime_type": "application/pdf"
},
"attachments": [
{
"document_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"file_name": "<string>",
"mime_type": "application/pdf"
}
]
},
"created_at": "2023-11-07T05:31:56Z"
}{
"error": {
"code": "invalid_request",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "invalid_token",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "token_disabled",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "duplicate_invoice",
"message": "<string>",
"request_id": "<string>",
"existing_invoice_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"description": "<string>"
}
}{
"error": {
"code": "length_required",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "payload_too_large",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "unsupported_media_type",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "rate_limit_exceeded",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "internal_error",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}{
"error": {
"code": "service_unavailable",
"message": "<string>",
"request_id": "<string>",
"description": "<string>"
}
}Authorizations
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>.
Headers
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.