Download OpenAPI specification:
The public API lets customers reach their own Campbooks data — email, documents, contacts, tags, document types, and Scout chat — from their own apps and scripts.
The API uses OAuth 2.0 client credentials. A credential acts as the user who created it, inside that user's workspace — there is no per-end-user authorization step.
POST /api/oauth/token. Always pass a scope parameter — a token
requested with no scope can call nothing.Authorization: Bearer <token> on every /api/v1
request.Scopes are a ceiling, not a grant of new power: a request must satisfy
both the token's scope and the acting user's own permissions (e.g.
emails:send still requires that the user may send from the chosen account).
{ "data": { … } }; paginated
collections are { "data": [ … ], "meta": { … } }.?page= and ?per_page= (default 25, max 100). A few
small reference collections (document types, Scout messages) are returned
unpaginated and carry no meta.{ "error": { "code": "…", "message": "…" } }, with validation
details under error.details where relevant.Exchange a client ID and secret for a short-lived bearer token (valid 2 hours) via the client_credentials grant. Always include scope.
| grant_type required | string Value: "client_credentials" |
| client_id required | string |
| client_secret required | string |
| scope | string Space-delimited list of scopes (e.g. "emails:read documents:read"). |
| access_token | string |
| token_type | string |
| expires_in | integer |
| scope | string |
| created_at | integer Unix timestamp. |
{- "access_token": "string",
- "token_type": "Bearer",
- "expires_in": 7200,
- "scope": "emails:read documents:read",
- "created_at": 0
}Revoke a single access token. Returns 200 with an empty body.
| token required | string |
| client_id required | string |
| client_secret required | string |
{- "error": {
- "code": "not_found",
- "message": "string",
- "details": { }
}
}| account_ids[] | Array of integers Restrict to one or more email account IDs. |
| unread | boolean |
| has_attachment | boolean |
| category | string |
| priority | string Enum: "low" "medium" "high" |
| q | string Matches subject or sender address. |
| received_after | string <date-time> ISO 8601 timestamp. |
| received_before | string <date-time> |
| page | integer >= 1 Default: 1 Page number (1-based). |
| per_page | integer [ 1 .. 100 ] Default: 25 Items per page (max 100). |
Array of objects (Email) | |
object (PageMeta) |
{- "data": [
- {
- "id": 0,
- "subject": "string",
- "from": "string",
- "to": "string",
- "cc": "string",
- "read": true,
- "has_attachment": true,
- "priority": "low",
- "category": "string",
- "summary": "string",
- "pinned": true,
- "received_at": "2019-08-24T14:15:22Z",
- "thread_id": 0,
- "account_id": 0,
- "tags": [
- "string"
]
}
], - "meta": {
- "page": 0,
- "per_page": 0,
- "total": 0,
- "total_pages": 0
}
}| email_account_id required | integer An account the acting user may send from. |
| to_address required | string |
| subject | string |
| body | string |
| cc_address | string |
| bcc_address | string |
object | |||||
| |||||
{- "email_account_id": 0,
- "to_address": "string",
- "subject": "string",
- "body": "string",
- "cc_address": "string",
- "bcc_address": "string"
}{- "data": {
- "id": 0,
- "provider_message_id": "string"
}
}Same fields as the list item, plus bcc and the full HTML body.
| id required | integer |
object (EmailDetail) | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
{- "data": {
- "id": 0,
- "subject": "string",
- "from": "string",
- "to": "string",
- "cc": "string",
- "read": true,
- "has_attachment": true,
- "priority": "low",
- "category": "string",
- "summary": "string",
- "pinned": true,
- "received_at": "2019-08-24T14:15:22Z",
- "thread_id": 0,
- "account_id": 0,
- "tags": [
- "string"
], - "bcc": "string",
- "body": "string"
}
}Marks the message read and syncs the flag to the provider mailbox.
| id required | integer |
object (EmailDetail) | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
{- "data": {
- "id": 0,
- "subject": "string",
- "from": "string",
- "to": "string",
- "cc": "string",
- "read": true,
- "has_attachment": true,
- "priority": "low",
- "category": "string",
- "summary": "string",
- "pinned": true,
- "received_at": "2019-08-24T14:15:22Z",
- "thread_id": 0,
- "account_id": 0,
- "tags": [
- "string"
], - "bcc": "string",
- "body": "string"
}
}Local-only — there is no cross-provider "mark unread" path.
| id required | integer |
object (EmailDetail) | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
{- "data": {
- "id": 0,
- "subject": "string",
- "from": "string",
- "to": "string",
- "cc": "string",
- "read": true,
- "has_attachment": true,
- "priority": "low",
- "category": "string",
- "summary": "string",
- "pinned": true,
- "received_at": "2019-08-24T14:15:22Z",
- "thread_id": 0,
- "account_id": 0,
- "tags": [
- "string"
], - "bcc": "string",
- "body": "string"
}
}Threads automatically via the source message. Sends from the source message's account unless email_account_id is given; replies to the original sender unless to_address is given.
| id required | integer |
| body required | string |
| to_address | string |
| cc_address | string |
| bcc_address | string |
| email_account_id | integer |
object | |||||
| |||||
{- "body": "string",
- "to_address": "string",
- "cc_address": "string",
- "bcc_address": "string",
- "email_account_id": 0
}{- "data": {
- "id": 0,
- "provider_message_id": "string"
}
}Documents are workspace-wide — every member sees them all.
| type | integer Document type ID. |
| review_status | string Enum: "pending" "approved" "rejected" |
| ai_status | string Enum: "pending" "processing" "completed" "failed" |
| page | integer >= 1 Default: 1 Page number (1-based). |
| per_page | integer [ 1 .. 100 ] Default: 25 Items per page (max 100). |
Array of objects (Document) | |
object (PageMeta) |
{- "data": [
- {
- "id": 0,
- "title": "string",
- "document_type": { },
- "document_type_id": 0,
- "ai_status": "pending",
- "review_status": "pending",
- "source": "manual_upload",
- "starred": true,
- "document_date": "2019-08-24",
- "vendor_name": "string",
- "client_name": "string",
- "invoice_number": "string",
- "amount_cents": 0,
- "currency": "string",
- "description": "string",
- "canonical_filename": "string",
- "created_at": "2019-08-24T14:15:22Z"
}
], - "meta": {
- "page": 0,
- "per_page": 0,
- "total": 0,
- "total_pages": 0
}
}Upload one or more files. AI classification/extraction runs asynchronously, so the response is 202 Accepted and each document starts with ai_status: "pending".
| files[] required | Array of strings <binary> [ items <binary > ] |
{- "data": [
- {
- "id": 0,
- "title": "string",
- "document_type": { },
- "document_type_id": 0,
- "ai_status": "pending",
- "review_status": "pending",
- "source": "manual_upload",
- "starred": true,
- "document_date": "2019-08-24",
- "vendor_name": "string",
- "client_name": "string",
- "invoice_number": "string",
- "amount_cents": 0,
- "currency": "string",
- "description": "string",
- "canonical_filename": "string",
- "created_at": "2019-08-24T14:15:22Z"
}
]
}Includes the attached file's metadata and the raw AI extraction data.
| id required | integer |
object (DocumentDetail) | |||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||
{- "data": {
- "id": 0,
- "title": "string",
- "document_type": { },
- "document_type_id": 0,
- "ai_status": "pending",
- "review_status": "pending",
- "source": "manual_upload",
- "starred": true,
- "document_date": "2019-08-24",
- "vendor_name": "string",
- "client_name": "string",
- "invoice_number": "string",
- "amount_cents": 0,
- "currency": "string",
- "description": "string",
- "canonical_filename": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "file": {
- "filename": "string",
- "content_type": "string",
- "byte_size": 0,
- "download_path": "string"
}, - "extraction": { }
}
}Edit extracted fields. Does not change review state (use approve / reclassify for that). Only the fields you send change.
| id required | integer |
| document_type_id | integer |
| vendor_name | string |
| vendor_nif | string |
| document_date | string <date> |
| due_date | string <date> |
| invoice_number | string |
| amount_cents | integer |
| currency | string |
| buyer_nif | string |
| tax_amount_cents | integer |
| tax_rate | number |
| description | string |
| expense_category | string |
| company_vat_present | boolean |
| client_name | string |
| client_nif | string |
| bank_name | string |
| account_number | string |
| period_start | string <date> |
| period_end | string <date> |
| opening_balance_cents | integer |
| closing_balance_cents | integer |
| receipt_number | string |
| payment_method | string |
object |
object (DocumentDetail) | |||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||
{- "document_type_id": 0,
- "vendor_name": "string",
- "vendor_nif": "string",
- "document_date": "2019-08-24",
- "due_date": "2019-08-24",
- "invoice_number": "string",
- "amount_cents": 0,
- "currency": "string",
- "buyer_nif": "string",
- "tax_amount_cents": 0,
- "tax_rate": 0,
- "description": "string",
- "expense_category": "string",
- "company_vat_present": true,
- "client_name": "string",
- "client_nif": "string",
- "bank_name": "string",
- "account_number": "string",
- "period_start": "2019-08-24",
- "period_end": "2019-08-24",
- "opening_balance_cents": 0,
- "closing_balance_cents": 0,
- "receipt_number": "string",
- "payment_method": "string",
- "metadata": { }
}{- "data": {
- "id": 0,
- "title": "string",
- "document_type": { },
- "document_type_id": 0,
- "ai_status": "pending",
- "review_status": "pending",
- "source": "manual_upload",
- "starred": true,
- "document_date": "2019-08-24",
- "vendor_name": "string",
- "client_name": "string",
- "invoice_number": "string",
- "amount_cents": 0,
- "currency": "string",
- "description": "string",
- "canonical_filename": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "file": {
- "filename": "string",
- "content_type": "string",
- "byte_size": 0,
- "download_path": "string"
}, - "extraction": { }
}
}Streams the original uploaded file. Send the same bearer token. Returns 404 (no_file) if the document has no attached file.
| id required | integer |
{- "error": {
- "code": "not_found",
- "message": "string",
- "details": { }
}
}Approve a document under review; records the acting user as the reviewer.
| id required | integer |
object (DocumentDetail) | |||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||
{- "data": {
- "id": 0,
- "title": "string",
- "document_type": { },
- "document_type_id": 0,
- "ai_status": "pending",
- "review_status": "pending",
- "source": "manual_upload",
- "starred": true,
- "document_date": "2019-08-24",
- "vendor_name": "string",
- "client_name": "string",
- "invoice_number": "string",
- "amount_cents": 0,
- "currency": "string",
- "description": "string",
- "canonical_filename": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "file": {
- "filename": "string",
- "content_type": "string",
- "byte_size": 0,
- "download_path": "string"
}, - "extraction": { }
}
}| id required | integer |
object (DocumentDetail) | |||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||
{- "data": {
- "id": 0,
- "title": "string",
- "document_type": { },
- "document_type_id": 0,
- "ai_status": "pending",
- "review_status": "pending",
- "source": "manual_upload",
- "starred": true,
- "document_date": "2019-08-24",
- "vendor_name": "string",
- "client_name": "string",
- "invoice_number": "string",
- "amount_cents": 0,
- "currency": "string",
- "description": "string",
- "canonical_filename": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "file": {
- "filename": "string",
- "content_type": "string",
- "byte_size": 0,
- "download_path": "string"
}, - "extraction": { }
}
}Change the document type. Reclassifying also signs the document off (it becomes approved).
| id required | integer |
| document_type_id required | integer |
object (DocumentDetail) | |||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||
{- "document_type_id": 0
}{- "data": {
- "id": 0,
- "title": "string",
- "document_type": { },
- "document_type_id": 0,
- "ai_status": "pending",
- "review_status": "pending",
- "source": "manual_upload",
- "starred": true,
- "document_date": "2019-08-24",
- "vendor_name": "string",
- "client_name": "string",
- "invoice_number": "string",
- "amount_cents": 0,
- "currency": "string",
- "description": "string",
- "canonical_filename": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "file": {
- "filename": "string",
- "content_type": "string",
- "byte_size": 0,
- "download_path": "string"
}, - "extraction": { }
}
}Contacts are auto-created from email sync (no create endpoint) and are workspace-wide.
| list_status | string Enum: "neutral" "allowed" "blocked" |
| starred | boolean |
| q | string Matches name or email. |
| page | integer >= 1 Default: 1 Page number (1-based). |
| per_page | integer [ 1 .. 100 ] Default: 25 Items per page (max 100). |
Array of objects (Contact) | |
object (PageMeta) |
{- "data": [
- {
- "id": 0,
- "email": "string",
- "name": "string",
- "organization": "string",
- "relationship_type": "self",
- "list_status": "neutral",
- "starred": true,
- "email_count": 0,
- "last_email_at": "2019-08-24T14:15:22Z",
- "context_summary": "string",
- "person_id": 0
}
], - "meta": {
- "page": 0,
- "per_page": 0,
- "total": 0,
- "total_pages": 0
}
}| id required | integer |
object (Contact) | |||||||||||||||||||||||
| |||||||||||||||||||||||
{- "data": {
- "id": 0,
- "email": "string",
- "name": "string",
- "organization": "string",
- "relationship_type": "self",
- "list_status": "neutral",
- "starred": true,
- "email_count": 0,
- "last_email_at": "2019-08-24T14:15:22Z",
- "context_summary": "string",
- "person_id": 0
}
}Only the fields you send change.
| id required | integer |
| name | string |
| relationship_type | string Enum: "self" "client" "vendor" "partner" "service_provider" "colleague" "personal" "unknown" |
object (Contact) | |||||||||||||||||||||||
| |||||||||||||||||||||||
{- "name": "string",
- "relationship_type": "self"
}{- "data": {
- "id": 0,
- "email": "string",
- "name": "string",
- "organization": "string",
- "relationship_type": "self",
- "list_status": "neutral",
- "starred": true,
- "email_count": 0,
- "last_email_at": "2019-08-24T14:15:22Z",
- "context_summary": "string",
- "person_id": 0
}
}Blocking also archives the contact's inbox messages.
| id required | integer |
| state required | string Enum: "star" "unstar" "allow" "block" "unblock" |
object (Contact) | |||||||||||||||||||||||
| |||||||||||||||||||||||
{- "state": "star"
}{- "data": {
- "id": 0,
- "email": "string",
- "name": "string",
- "organization": "string",
- "relationship_type": "self",
- "list_status": "neutral",
- "starred": true,
- "email_count": 0,
- "last_email_at": "2019-08-24T14:15:22Z",
- "context_summary": "string",
- "person_id": 0
}
}Tags apply to emails only (documents are organized by document type).
| page | integer >= 1 Default: 1 Page number (1-based). |
| per_page | integer [ 1 .. 100 ] Default: 25 Items per page (max 100). |
Array of objects (Tag) | |
object (PageMeta) |
{- "data": [
- {
- "id": 0,
- "name": "string",
- "color": "string",
- "group_name": "string",
- "source": "string",
- "email_account_id": 0
}
], - "meta": {
- "page": 0,
- "per_page": 0,
- "total": 0,
- "total_pages": 0
}
}The workspace's document types — use a type's id with document upload, update, and reclassify. Returned unpaginated (no meta).
Array of objects (DocumentType) | |||||||||||||
Array
| |||||||||||||
{- "data": [
- {
- "id": 0,
- "name": "string",
- "color": "string",
- "category": "string",
- "auto_star": true,
- "extraction_schema": { }
}
]
}The acting user's chat threads, newest-first.
| page | integer >= 1 Default: 1 Page number (1-based). |
| per_page | integer [ 1 .. 100 ] Default: 25 Items per page (max 100). |
Array of objects (AgentThread) | |
object (PageMeta) |
{- "data": [
- {
- "id": 0,
- "title": "string",
- "purpose": "global",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
], - "meta": {
- "page": 0,
- "per_page": 0,
- "total": 0,
- "total_pages": 0
}
}| title | string Default: "New chat" |
object (AgentThread) | |||||||||||
| |||||||||||
{- "title": "New chat"
}{- "data": {
- "id": 0,
- "title": "string",
- "purpose": "global",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}
}Messages in chronological order, unpaginated (no meta). Pass after_message_id to fetch only messages created after that one — the poll loop for the async AI reply.
| thread_id required | integer |
| after_message_id | integer Return only messages created after this message ID. |
Array of objects (AgentMessage) | |||||||||||||||||
Array
| |||||||||||||||||
{- "data": [
- {
- "id": 0,
- "thread_id": 0,
- "author_type": "user",
- "content": "string",
- "reply_status": "pending",
- "suggested_actions": [
- { }
], - "prompts": [
- "string"
], - "created_at": "2019-08-24T14:15:22Z"
}
]
}Post a user message and kick off the async AI reply. Returns 202 Accepted with the created user message. The AI reply lands later as a new message with author_type: "ai" and reply_status: "replied" — poll the GET endpoint with after_message_id set to the returned ID.
| thread_id required | integer |
| content required | string |
{- "content": "string"
}{- "data": {
- "id": 0,
- "thread_id": 0,
- "author_type": "user",
- "content": "string",
- "reply_status": "pending",
- "suggested_actions": [
- { }
], - "prompts": [
- "string"
], - "created_at": "2019-08-24T14:15:22Z"
}
}