Campbooks API reference

Campbooks Public REST API (1.0.0)

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.

Authentication

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.

  1. In the app, go to Settings → API access and create a client. Pick the scopes it needs. The client secret is shown once — store it safely (it's hashed at rest and can be regenerated, not re-displayed).
  2. Exchange the credentials for a short-lived bearer token (valid 2 hours) at POST /api/oauth/token. Always pass a scope parameter — a token requested with no scope can call nothing.
  3. Send the token as 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).

Conventions

  • Format: JSON. Single resources are { "data": { … } }; paginated collections are { "data": [ … ], "meta": { … } }.
  • Pagination: ?page= and ?per_page= (default 25, max 100). A few small reference collections (document types, Scout messages) are returned unpaginated and carry no meta.
  • Rate limit: 600 requests/minute per client (HTTP 429 when exceeded).
  • Errors: { "error": { "code": "…", "message": "…" } }, with validation details under error.details where relevant.
  • 404, not 403, is returned for resources you can't see, so the API never reveals the existence of another workspace's data.

OAuth

Token issuance and revocation.

Issue a bearer token

Exchange a client ID and secret for a short-lived bearer token (valid 2 hours) via the client_credentials grant. Always include scope.

Request Body schema: application/x-www-form-urlencoded
required
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").

Responses

Response Schema: application/json
access_token
string
token_type
string
expires_in
integer
scope
string
created_at
integer

Unix timestamp.

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "token_type": "Bearer",
  • "expires_in": 7200,
  • "scope": "emails:read documents:read",
  • "created_at": 0
}

Revoke a token

Revoke a single access token. Returns 200 with an empty body.

Request Body schema: application/x-www-form-urlencoded
required
token
required
string
client_id
required
string
client_secret
required
string

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Emails

List, read, send, and reply to email; mark read/unread.

List emails

Authorizations:
oauth2
query Parameters
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).

Responses

Response Schema: application/json
Array of objects (Email)
object (PageMeta)

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "meta": {
    }
}

Send an email

Authorizations:
oauth2
Request Body schema: application/json
required
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

Responses

Response Schema: application/json
object
id
integer or null

The stored EmailMessage ID, when one was created.

provider_message_id
string or null

Request samples

Content type
application/json
{
  • "email_account_id": 0,
  • "to_address": "string",
  • "subject": "string",
  • "body": "string",
  • "cc_address": "string",
  • "bcc_address": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Get an email

Same fields as the list item, plus bcc and the full HTML body.

Authorizations:
oauth2
path Parameters
id
required
integer

Responses

Response Schema: application/json
object (EmailDetail)
id
integer
subject
string or null
from
string or null
to
string or null
cc
string or null
read
boolean
has_attachment
boolean
priority
string
Enum: "low" "medium" "high"
category
string or null
summary
string or null

AI-generated one-line summary.

pinned
boolean
received_at
string or null <date-time>
thread_id
integer or null
account_id
integer
tags
Array of strings
bcc
string or null
body
string or null

Full HTML body.

Response samples

Content type
application/json
{
  • "data": {
    }
}

Mark an email read

Marks the message read and syncs the flag to the provider mailbox.

Authorizations:
oauth2
path Parameters
id
required
integer

Responses

Response Schema: application/json
object (EmailDetail)
id
integer
subject
string or null
from
string or null
to
string or null
cc
string or null
read
boolean
has_attachment
boolean
priority
string
Enum: "low" "medium" "high"
category
string or null
summary
string or null

AI-generated one-line summary.

pinned
boolean
received_at
string or null <date-time>
thread_id
integer or null
account_id
integer
tags
Array of strings
bcc
string or null
body
string or null

Full HTML body.

Response samples

Content type
application/json
{
  • "data": {
    }
}

Mark an email unread

Local-only — there is no cross-provider "mark unread" path.

Authorizations:
oauth2
path Parameters
id
required
integer

Responses

Response Schema: application/json
object (EmailDetail)
id
integer
subject
string or null
from
string or null
to
string or null
cc
string or null
read
boolean
has_attachment
boolean
priority
string
Enum: "low" "medium" "high"
category
string or null
summary
string or null

AI-generated one-line summary.

pinned
boolean
received_at
string or null <date-time>
thread_id
integer or null
account_id
integer
tags
Array of strings
bcc
string or null
body
string or null

Full HTML body.

Response samples

Content type
application/json
{
  • "data": {
    }
}

Reply to an email

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.

Authorizations:
oauth2
path Parameters
id
required
integer
Request Body schema: application/json
required
body
required
string
to_address
string
cc_address
string
bcc_address
string
email_account_id
integer

Responses

Response Schema: application/json
object
id
integer or null

The stored EmailMessage ID, when one was created.

provider_message_id
string or null

Request samples

Content type
application/json
{
  • "body": "string",
  • "to_address": "string",
  • "cc_address": "string",
  • "bcc_address": "string",
  • "email_account_id": 0
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Email tags

Attach and detach workspace tags on an email.

Add a tag to an email

Provide tag_id or name. The tag must already exist in the workspace — tags are not created here.

Authorizations:
oauth2
path Parameters
email_id
required
integer
Request Body schema: application/json
required
tag_id
integer
name
string

Responses

Response Schema: application/json
object (Tag)
id
integer
name
string
color
string or null
group_name
string or null
source
string

How the tag was created (e.g. user, ai).

email_account_id
integer or null

Set when the tag is scoped to a single email account.

Request samples

Content type
application/json
{
  • "tag_id": 0,
  • "name": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Remove a tag from an email

Authorizations:
oauth2
path Parameters
email_id
required
integer
id
required
integer

The tag ID to remove.

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Documents

List, read, upload, edit, approve, reject, and reclassify documents.

List documents

Documents are workspace-wide — every member sees them all.

Authorizations:
oauth2
query Parameters
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).

Responses

Response Schema: application/json
Array of objects (Document)
object (PageMeta)

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "meta": {
    }
}

Upload documents

Upload one or more files. AI classification/extraction runs asynchronously, so the response is 202 Accepted and each document starts with ai_status: "pending".

Authorizations:
oauth2
Request Body schema: multipart/form-data
required
files[]
required
Array of strings <binary> [ items <binary > ]

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Get a document

Includes the attached file's metadata and the raw AI extraction data.

Authorizations:
oauth2
path Parameters
id
required
integer

Responses

Response Schema: application/json
object (DocumentDetail)
id
integer
title
string or null
document_type
object or null

The full document type record (or null when unclassified).

document_type_id
integer or null
ai_status
string
Enum: "pending" "processing" "completed" "failed"
review_status
string
Enum: "pending" "approved" "rejected"
source
string
Enum: "manual_upload" "email" "notion"
starred
boolean
document_date
string or null <date>
vendor_name
string or null
client_name
string or null
invoice_number
string or null
amount_cents
integer or null
currency
string or null
description
string or null
canonical_filename
string or null
created_at
string <date-time>
object or null
object or null

Raw AI extraction data (shape varies by document type).

Response samples

Content type
application/json
{
  • "data": {
    }
}

Update document fields

Edit extracted fields. Does not change review state (use approve / reclassify for that). Only the fields you send change.

Authorizations:
oauth2
path Parameters
id
required
integer
Request Body schema: application/json
required
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

Responses

Response Schema: application/json
object (DocumentDetail)
id
integer
title
string or null
document_type
object or null

The full document type record (or null when unclassified).

document_type_id
integer or null
ai_status
string
Enum: "pending" "processing" "completed" "failed"
review_status
string
Enum: "pending" "approved" "rejected"
source
string
Enum: "manual_upload" "email" "notion"
starred
boolean
document_date
string or null <date>
vendor_name
string or null
client_name
string or null
invoice_number
string or null
amount_cents
integer or null
currency
string or null
description
string or null
canonical_filename
string or null
created_at
string <date-time>
object or null
object or null

Raw AI extraction data (shape varies by document type).

Request samples

Content type
application/json
{
  • "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": { }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Download a document's file

Streams the original uploaded file. Send the same bearer token. Returns 404 (no_file) if the document has no attached file.

Authorizations:
oauth2
path Parameters
id
required
integer

Responses

Response Schema: application/octet-stream
string <binary>

Response samples

Content type
application/json
{
  • "error": {
    }
}

Approve a document

Approve a document under review; records the acting user as the reviewer.

Authorizations:
oauth2
path Parameters
id
required
integer

Responses

Response Schema: application/json
object (DocumentDetail)
id
integer
title
string or null
document_type
object or null

The full document type record (or null when unclassified).

document_type_id
integer or null
ai_status
string
Enum: "pending" "processing" "completed" "failed"
review_status
string
Enum: "pending" "approved" "rejected"
source
string
Enum: "manual_upload" "email" "notion"
starred
boolean
document_date
string or null <date>
vendor_name
string or null
client_name
string or null
invoice_number
string or null
amount_cents
integer or null
currency
string or null
description
string or null
canonical_filename
string or null
created_at
string <date-time>
object or null
object or null

Raw AI extraction data (shape varies by document type).

Response samples

Content type
application/json
{
  • "data": {
    }
}

Reject a document

Authorizations:
oauth2
path Parameters
id
required
integer

Responses

Response Schema: application/json
object (DocumentDetail)
id
integer
title
string or null
document_type
object or null

The full document type record (or null when unclassified).

document_type_id
integer or null
ai_status
string
Enum: "pending" "processing" "completed" "failed"
review_status
string
Enum: "pending" "approved" "rejected"
source
string
Enum: "manual_upload" "email" "notion"
starred
boolean
document_date
string or null <date>
vendor_name
string or null
client_name
string or null
invoice_number
string or null
amount_cents
integer or null
currency
string or null
description
string or null
canonical_filename
string or null
created_at
string <date-time>
object or null
object or null

Raw AI extraction data (shape varies by document type).

Response samples

Content type
application/json
{
  • "data": {
    }
}

Reclassify a document

Change the document type. Reclassifying also signs the document off (it becomes approved).

Authorizations:
oauth2
path Parameters
id
required
integer
Request Body schema: application/json
required
document_type_id
required
integer

Responses

Response Schema: application/json
object (DocumentDetail)
id
integer
title
string or null
document_type
object or null

The full document type record (or null when unclassified).

document_type_id
integer or null
ai_status
string
Enum: "pending" "processing" "completed" "failed"
review_status
string
Enum: "pending" "approved" "rejected"
source
string
Enum: "manual_upload" "email" "notion"
starred
boolean
document_date
string or null <date>
vendor_name
string or null
client_name
string or null
invoice_number
string or null
amount_cents
integer or null
currency
string or null
description
string or null
canonical_filename
string or null
created_at
string <date-time>
object or null
object or null

Raw AI extraction data (shape varies by document type).

Request samples

Content type
application/json
{
  • "document_type_id": 0
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Contacts

List and read contacts; edit and change their state.

List contacts

Contacts are auto-created from email sync (no create endpoint) and are workspace-wide.

Authorizations:
oauth2
query Parameters
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).

Responses

Response Schema: application/json
Array of objects (Contact)
object (PageMeta)

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "meta": {
    }
}

Get a contact

Authorizations:
oauth2
path Parameters
id
required
integer

Responses

Response Schema: application/json
object (Contact)
id
integer
email
string or null
name
string or null
organization
string or null
relationship_type
string or null
Enum: "self" "client" "vendor" "partner" "service_provider" "colleague" "personal" "unknown"
list_status
string
Enum: "neutral" "allowed" "blocked"
starred
boolean
email_count
integer
last_email_at
string or null <date-time>
context_summary
string or null
person_id
integer or null

Response samples

Content type
application/json
{
  • "data": {
    }
}

Update a contact

Only the fields you send change.

Authorizations:
oauth2
path Parameters
id
required
integer
Request Body schema: application/json
required
name
string
relationship_type
string
Enum: "self" "client" "vendor" "partner" "service_provider" "colleague" "personal" "unknown"

Responses

Response Schema: application/json
object (Contact)
id
integer
email
string or null
name
string or null
organization
string or null
relationship_type
string or null
Enum: "self" "client" "vendor" "partner" "service_provider" "colleague" "personal" "unknown"
list_status
string
Enum: "neutral" "allowed" "blocked"
starred
boolean
email_count
integer
last_email_at
string or null <date-time>
context_summary
string or null
person_id
integer or null

Request samples

Content type
application/json
{
  • "name": "string",
  • "relationship_type": "self"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Change a contact's state

Blocking also archives the contact's inbox messages.

Authorizations:
oauth2
path Parameters
id
required
integer
Request Body schema: application/json
required
state
required
string
Enum: "star" "unstar" "allow" "block" "unblock"

Responses

Response Schema: application/json
object (Contact)
id
integer
email
string or null
name
string or null
organization
string or null
relationship_type
string or null
Enum: "self" "client" "vendor" "partner" "service_provider" "colleague" "personal" "unknown"
list_status
string
Enum: "neutral" "allowed" "blocked"
starred
boolean
email_count
integer
last_email_at
string or null <date-time>
context_summary
string or null
person_id
integer or null

Request samples

Content type
application/json
{
  • "state": "star"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Tags

List the workspace's tags.

List tags

Tags apply to emails only (documents are organized by document type).

Authorizations:
oauth2
query Parameters
page
integer >= 1
Default: 1

Page number (1-based).

per_page
integer [ 1 .. 100 ]
Default: 25

Items per page (max 100).

Responses

Response Schema: application/json
Array of objects (Tag)
object (PageMeta)

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "meta": {
    }
}

Document types

List the workspace's document types.

List document types

The workspace's document types — use a type's id with document upload, update, and reclassify. Returned unpaginated (no meta).

Authorizations:
oauth2

Responses

Response Schema: application/json
Array of objects (DocumentType)
Array
id
integer
name
string
color
string or null
category
string or null
auto_star
boolean
object or null

The fields the AI extracts for this type.

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Scout

The in-app AI assistant — async chat threads and messages.

List Scout threads

The acting user's chat threads, newest-first.

Authorizations:
oauth2
query Parameters
page
integer >= 1
Default: 1

Page number (1-based).

per_page
integer [ 1 .. 100 ]
Default: 25

Items per page (max 100).

Responses

Response Schema: application/json
Array of objects (AgentThread)
object (PageMeta)

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "meta": {
    }
}

Create a Scout thread

Authorizations:
oauth2
Request Body schema: application/json
optional
title
string
Default: "New chat"

Responses

Response Schema: application/json
object (AgentThread)
id
integer
title
string or null
purpose
string
Enum: "global" "email_chat" "compose_chat"
created_at
string <date-time>
updated_at
string <date-time>

Request samples

Content type
application/json
{
  • "title": "New chat"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Read / poll thread messages

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.

Authorizations:
oauth2
path Parameters
thread_id
required
integer
query Parameters
after_message_id
integer

Return only messages created after this message ID.

Responses

Response Schema: application/json
Array of objects (AgentMessage)
Array
id
integer
thread_id
integer
author_type
string
Enum: "user" "ai"
content
string or null
reply_status
string
Enum: "pending" "processing" "replied" "failed"
Array of objects or null

Actions Scout proposed (shape varies).

prompts
Array of strings or null

Suggested follow-up prompts.

created_at
string <date-time>

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Send a message

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.

Authorizations:
oauth2
path Parameters
thread_id
required
integer
Request Body schema: application/json
required
content
required
string

Responses

Request samples

Content type
application/json
{
  • "content": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}