Dropbox Sensor
What it ingests
Files, folders, and shared-folder members from a Dropbox Business team, through the Dropbox API (https://api.dropboxapi.com/2). It uses the same file-store mapping as the Box sensor:
| Dropbox object | Ontology entity | Signal |
|---|---|---|
| File | CreativeWork (source_type: document) | file |
| Folder, including a team folder or shared folder's root | CreativeWork (source_type: folder) | folder |
| Last modifier, shared-folder member | Person (identity) | — |
And these edges:
- the last modifier
AUTHOREDa file; - a file is
PART_OFits folder, and a folder isPART_OFits parent folder, up to the team folder or shared folder it lives in; - a member of a team folder or shared folder
WORKS_ONit when they are anowneroreditor, or isRELATED_TOit when they are aviewer(with or without comment rights). The access level is on the edge either way. Members are notMEMBER_OF, which is reserved for membership of an organization or team.
People resolve to existing Person vertices by email, so a Dropbox editor who is also a Slack author or a GitHub committer is one person in the graph.
Entities are named by their folder and path — Engineering/Specs/Plan.docx, not Plan.docx — because the same file name exists in every team's folders. External ids are stable: dropbox:file:<id> for a file, and dropbox:folder:<namespace id>:<path> for a folder. 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, at most 50 files per poll. Other files, including Office documents, Paper docs and PDFs, land with their title, path, URL and modifier but no body. This is the same rule as SharePoint / OneDrive and Box.
The mapping is deterministic. No LLM runs per file.
Credential
References a dropbox_team_oauth credential by id. The secret is a JSON object for a team-scoped Dropbox app, holding the long-lived refresh token a team admin's authorization produced:
{
"app_key": "abcd1234efgh567",
"app_secret": "…",
"refresh_token": "…"
}curl -sS -X POST https://your-corveil-host/api/credentials \
-H "Authorization: Bearer <admin>" -H "Content-Type: application/json" \
-d '{"type":"dropbox_team_oauth","name":"acme-dropbox","secret":"{\"app_key\":\"…\",\"app_secret\":\"…\",\"refresh_token\":\"…\"}"}'Corveil redeems the refresh token for a four-hour access token as needed. It reads team folders and shared folders as the admin who authorized the app, and (when you opt in) each member's own folder as that member. It never writes.
Short-lived tokens are refused
An access token that starts with sl. expires after four hours. It is refused when you save it, whether it is pasted on its own or in the refresh_token field. Authorize with token_access_type=offline (step 4 below) to get a refresh token.
Config fields
| Key | Required | Notes |
|---|---|---|
namespace_ids | no | Comma-separated namespace IDs to crawl (1234567890 or ns:1234567890). Blank means every team folder and shared folder the team owns. |
include_member_folders | no | true also crawls each member's own, unshared folder. Default false: personal folders often hold content nobody chose to share. A member folder listed in namespace_ids is crawled either way. |
file_extensions | no | Comma-separated extensions to ingest (docx, pdf, md). Other files are skipped. Blank ingests every file. |
There is no default_entity_type. Files and folders are told apart by source_type, so one override would give one of them the wrong meaning.
Finding a namespace ID
A namespace is a team folder, a shared folder, or a member's folder. To list a team's namespaces with their IDs, call POST https://api.dropboxapi.com/2/team/namespaces/list with the app's access token, or leave namespace_ids blank and read the namespace_id property on the folder entities after the first poll.
Provider-side setup
You need a Dropbox Team admin for step 4. A user or billing admin cannot authorize a team-scoped app to read members' files.
Open the Dropbox App Console → Create app → Scoped access → Full Dropbox. Name the app.
On Permissions, enable these scopes and click Submit:
- Team:
team_info.read,members.read,team_data.member,team_data.team_space - Files:
files.metadata.read,files.content.read - Sharing:
sharing.read
Nothing else is needed. The sensor never writes. Selecting any team scope makes the app team-scoped.
- Team:
On Settings, copy the App key and App secret.
As a team admin, open this URL in a browser (replace
APP_KEY), approve the app, and copy the authorization code it shows:https://www.dropbox.com/oauth2/authorize?client_id=APP_KEY&response_type=code&token_access_type=offlineExchange the code for a refresh token. The code is single-use and expires after a few minutes:
bashcurl -sS https://api.dropboxapi.com/oauth2/token \ -d code=AUTHORIZATION_CODE -d grant_type=authorization_code \ -u APP_KEY:APP_SECRETCopy
refresh_tokenfrom the response. Ignoreaccess_token: it expires in four hours.Store the app key, app secret, and refresh token as a
dropbox_team_oauthcredential.
The refresh token stays valid until an admin unlinks the app (Admin console → Settings → Third-party apps) or the authorizing admin leaves the team or loses the Team admin role. After that, Corveil reports an authentication error; repeat steps 4–6.
Wire-up
POST /api/sensors
{
"sensor_type": "dropbox",
"name": "Acme Dropbox",
"config": {
"namespace_ids": "",
"include_member_folders": "false",
"file_extensions": ""
},
"credentials": { "team_app": "<dropbox_team_oauth credential id>" }
}Verify
Run Test connection. It redeems the refresh token, reads the team (team/get_info) and the authorizing admin, and checks that each configured namespace exists. A revoked token or a wrong app secret fails as an authentication error. A user-scoped app (one with no team scopes) fails on the team read, and an app authorized by someone who is not a team admin fails on the admin read. A mistyped namespace ID fails with a message that names it.
The first poll then lists the team's folders. Each poll logs dropbox sensor: poll complete with namespaces, namespaces_listing, change_events, crawl_events and member_events. A namespaces_listing above zero means first listings are still running across ticks. In the graph, a team folder should appear as a folder entity with its members linked, and a file named <team folder>/<path> should be PART_OF its folder and AUTHORED by its last editor.
How incremental sync works
- One cursor per namespace. Each team folder, shared folder, and (opted in) member folder is listed recursively with
files/list_folder. When a listing finishes, its cursor reads only that namespace's changes from then on, throughfiles/list_folder/continue. - Each folder is listed once. Mounted folders are excluded, so a shared folder is listed as its own namespace, not again under every member who added it to their Dropbox.
- First listings resume across ticks. The sensor reads up to 20 listing pages of up to 2,000 entries per poll. A large team's first crawl continues where it stopped on the next tick.
- Change reads are budgeted separately (up to 200 calls per poll), so a long first listing does not delay changes in folders already listed. With more namespaces than that, the walk resumes where it stopped.
- Membership is re-read on a rotation. A member added to or removed from a shared folder does not show in its file changes, so each poll re-reads the member list of up to 25 shared and team folders. An unchanged list is deduplicated, and a changed one updates the graph.
- A new version is a new event, not a new document. Event ids carry the file's
rev(or a digest of a folder's path and members), so a change is ingested. The entity id staysdropbox:file:<id>, so the change updates the entity it already has. - A reset cursor is recovered. If Dropbox invalidates a namespace's cursor, the sensor lists that namespace again. Items already ingested are deduplicated.
- Rate limits. The sensor paces itself at about 600 requests a minute. On a 429 it ends the poll and keeps its cursor, so the next tick retries the same changes.
- Sync cadence is the sensor's poll interval. Changes appear within one interval.
Known limits
- Deletes are not propagated. A deleted file stays in the graph. The ontology has no delete path for sensor events.
- A renamed folder becomes a new folder entity. Dropbox names a file's parent only by path, so folders are keyed by path. Dropbox re-lists the renamed folder's contents, which then point at the new folder.
- Modifiers are known only inside shared and team folders. Dropbox reports who last changed a file only for shared content. In a member's own folder the member is the modifier. A modifier outside the team, known to Dropbox only by account id, gets no
AUTHOREDedge. - Shared folders the admin cannot open are skipped with a warning in the log. Add the admin who authorized the app as a member of the folder.
- Group members are not expanded, and pending invitations are skipped. Only users get member edges.
- File comments are not ingested. The Dropbox API has no comments endpoint.
- Paper docs, Office files and PDFs ingest without body text.
- Links are approximate. A file's URL assumes the folder is mounted under its own name, which a member can change.
Not included
- Webhooks and long-polling. Polling each namespace's cursor covers the same changes without a public endpoint.
- Team activity log (
team_log/get_events), file requests, and file properties/templates. - User-scoped apps. One person's Dropbox is not a team's; the sensor requires a team-scoped app.