Box Sensor
What it ingests
Files, folders, file comments, and folder collaborations from Box, through the Box Platform API (https://api.box.com/2.0):
| Box object | Ontology entity | Signal |
|---|---|---|
| File | CreativeWork (source_type: document) | file |
| Folder | CreativeWork (source_type: folder) | folder |
| File comment | Comment | comment |
| Modifier, creator, commenter, collaborator | Person (identity) | — |
And these edges:
- the last modifier — and the creator, when that is someone else —
AUTHOREDa file or folder; - a file is
PART_OFits folder, and a folder isPART_OFits parent folder; - a folder collaborator
WORKS_ONthe folder when their Box role can change content (editor,co-owner, the uploader roles), or isRELATED_TOit when the role is read-only (viewer,previewer). The role is on the edge either way. Collaborators are notMEMBER_OF, which is reserved for membership of an organization or team; - a commenter
AUTHOREDthe comment, and the comment isRELATED_TOthe file.
People resolve to existing Person vertices by email, so a Box editor who is also a Slack author or a GitHub committer is one person in the graph.
Entities are named by their path below All Files — Engineering/Specs/Plan.docx, not Plan.docx — because the same file name exists in every team's folders. The "All Files" root itself is never an entity. External ids are stable (box:file:<id>, box:folder:<id>, box:comment:<id>), so a new version of a file updates the same entity instead of creating another.
Body text is extracted from text-like files (.md, .txt, .csv, .json, .html, …) up to 1 MiB. Other files, including Office documents and PDFs, land with their title, path, URL, owner and authors but no body. This is the same rule as SharePoint / OneDrive.
The mapping is deterministic. No LLM runs per file.
Credential
References a box_client_credentials credential by id. The secret is a JSON object for a Box Platform app that uses Server Authentication (Client Credentials Grant):
{
"client_id": "abcdefghijklmnopqrstuvwxyz012345",
"client_secret": "…",
"enterprise_id": "123456789"
}curl -sS -X POST https://your-corveil-host/api/credentials \
-H "Authorization: Bearer <admin>" -H "Content-Type: application/json" \
-d '{"type":"box_client_credentials","name":"acme-box","secret":"{\"client_id\":\"…\",\"client_secret\":\"…\",\"enterprise_id\":\"123456789\"}"}'Corveil mints a token with box_subject_type=enterprise. The token acts as the app's service account (AutomationUser_…@boxdevedition.com). The sensor reads what that account can see. It never acts as an individual user.
JWT apps and developer tokens are not supported in v1
Box's other Server Authentication option, JWT, signs with an encrypted keypair. Its config.json is refused when you save it. Switch the app to Client Credentials Grant (same app, same scopes). A developer token expires after an hour and is refused for the same reason.
Config fields
| Key | Required | Notes |
|---|---|---|
folder_ids | no | Comma-separated Box folder IDs to crawl: the number at the end of app.box.com/folder/<id>. Blank (or 0) means everything the service account can see. |
file_extensions | no | Comma-separated extensions to ingest (docx, pdf, md). Other files, and their comments, are skipped. Blank ingests every file. |
There is no default_entity_type. Files, folders and comments are separate entity types, so one override would give the others the wrong type.
Provider-side setup
You need a Box admin (or co-admin with app-management rights) for step 4.
- Open the Box Developer Console → Create Platform App → Custom App. Choose Server Authentication (Client Credentials Grant).
- On Configuration:
- App Access Level: App + Enterprise Access.
- Application Scopes: Read all files and folders stored in Box. Nothing else is needed. The sensor never writes.
- Copy the Client ID and Client Secret. Box asks for 2-step verification before it shows the secret.
- On General Settings, copy the Enterprise ID. Note the Service Account email (
AutomationUser_…@boxdevedition.com). - On Authorization, click Review and Submit. An admin then approves the app in the Admin Console → Integrations → Platform Apps Manager (search by Client ID → Authorize). Until then, every token request fails with
unauthorized_client. - Share the content you want ingested with the service account. Invite its email as a Viewer collaborator on each folder, or on a parent folder. A new app's service account owns nothing and is collaborated on nothing, so it sees nothing until you share.
- Store the three values as a
box_client_credentialscredential.
Pick folders, not the whole enterprise
folder_ids blank means "everything shared with the service account". It does not mean every user's files. Box only exposes another user's content to the service account through a collaboration. That collaboration also decides what Corveil can read.
Wire-up
POST /api/sensors
{
"sensor_type": "box",
"name": "Acme Box",
"config": {
"folder_ids": "123456789, 987654321",
"file_extensions": ""
},
"credentials": { "app_credentials": "<box_client_credentials credential id>" }
}Verify
Run Test connection. It mints a token, reads the service account (GET /users/me) and opens each configured folder. If the app is not authorized, or the secret is wrong, it fails as an authentication error. If a folder is not shared with the service account, it fails with a message that names the folder.
The first poll then crawls the configured folders. Each poll logs box sensor: poll complete with stream_events, crawl_events and crawl_queue. crawl_queue above zero means the first crawl is still running across ticks. In the graph, a file named <folder path>/<file name> should appear, PART_OF its folder and AUTHORED by its last editor.
How incremental sync works
- The first poll records the event-stream position, then crawls. The position is read before the crawl starts, so a change made during a long first crawl is replayed from the stream afterwards, not lost.
- The crawl resumes across ticks. It lists up to 20 pages of up to 1,000 items per poll. The folders still to list, and the page within the current one, live in the cursor, so a large first crawl continues where it stopped.
- Later polls read the service account's
changesevent stream and re-fetch only the files and folders it names. Only events that can change the graph count: uploads, creates, moves, copies, renames, restores, comments, and collaborator changes. Downloads and previews do not, so viewing a file costs nothing. - A folder moved or copied into scope is crawled. The stream names only the folder, so its existing contents are queued for the crawl.
- A new version is a new event, not a new document. Event ids carry the file's
etag(a comment's edit time, or a digest of a folder's collaborators), so a change is ingested. The entity id staysbox:file:<id>, so the change updates the entity it already has. - Rate limits. Box limits each user, including the service account, to about 1,000 requests a minute. The sensor paces itself at about 600 a minute. On a 429 it ends the poll and keeps its cursor, so the next tick retries the same window.
- Sync cadence is the sensor's poll interval. Changes appear within one interval of the stream recording them.
Known limits
- Deletes are not propagated. A trashed or deleted item stays in the graph. The ontology has no delete path for sensor events.
- A rename or move does not rename the entity. The entity keeps its external id, and its properties (
name,path,url) update, but its display name stays the original path. This is the same as other document sources. - Only content shared with the service account. There is no enterprise-wide crawl through the admin events stream (
admin_logs) and noAs-Userimpersonation in v1. - Group collaborators are skipped. Only users get collaborator edges. Pending invitations are skipped too.
- Box Notes, Office files and PDFs ingest without body text.
- Box Shield classifications, metadata templates, tasks and web links are out of v1.
Not included
- JWT (keypair) authentication and OAuth 2.0 user auth. Client Credentials Grant is the server-to-server path Box recommends for new apps.
- Webhooks. Box webhooks are per-folder and would have to be created on every folder in scope. The event stream covers the same changes without that.