Browse the docs
Reference
API reference
Every endpoint on this page is live, and each one states the token scope it requires, what it returns, what can go wrong, and whether it can be undone. Base URL /api/v1. Updrone is the software underneath — your business stays the professional of record for its own flights, pilots, airspace, and accuracy sign-off.
Authentication & conventions#
Read this once and every endpoint below behaves the way you expect.
- Authenticate every request with an API token:
Authorization: Bearer sk_…. Issue tokens under Settings → API tokens (the secret is shown once, at creation). - A token acts as its role through the same permission gate as the portal UI. It can be issued against one of your own custom roles, narrowed per kind and per verb (
{job:["read"], deliverable:["create"]}), restricted to specific projects, bound to a member, and time-boxed — all under Settings → API tokens. It can never exceed the role it was issued against. - A project-scoped token answers 404 for a project outside its scope, exactly as it would for one in another tenant — existence is not disclosed to a caller that cannot reach it.
/api/portal/*is SESSION-ONLY and will refuse a Bearer token with 401no_session. Every machine-callable endpoint is listed on this page.- Requests are rate-limited per token on separate buckets: 120 reads/min and 20 writes/min. A 429 carries
code: "rate_limited"and a Retry-After header — back off and retry.X-RateLimit-Bucketon every response names which budget you are spending. - SEPARATE from the request limit, a CONSEQUENCE budget caps what can leave the system per hour, per token, per kind: 30 emails, 20 SMS, 10 charges, 10 publishes, 20 compute runs. One request can carry many consequences, and the ones that reach your customers are the ones that matter — a looping caller inside the write limit could still email forty people a minute. Exhausting one kind is a 429 naming that kind, so you can keep doing the parts that are still allowed.
- An operation that is
irreversibleorbindingrequires a TWO-PHASE CONFIRM. The first call answers409 confirmation_requiredwith aconfirmTokenand awillDodescription of what it is about to do; re-send the identical request withx-updrone-confirm: <token>to perform it. The token binds the tenant, the token, the operation AND the exact payload, so it cannot be replayed against a different body, and it expires in 10 minutes. SetX-Agent-Confirmed-Byto record who authorised it — self-reported, and stored as a claim, not as proof. - Send
x-updrone-dry-run: 1to an endpoint that supports it to compute the effects and apply nothing; the response is{ok, dryRun: true, applied: false, willDo, effects}. An endpoint that cannot rehearse REFUSES the header rather than ignoring it — a rehearsal that silently applies is the one outcome the header must never produce. - A state change goes through the entity's own machine and a v1 route never invents a transition. An illegal trigger is
409 conflictcarryinglegalTriggers(orlegalTransitions) — what IS legal from here — so you re-plan from one response instead of probing. A trigger whose destination the entity is ALREADY in answers 200 withreplayed: true, because a retry after a lost response is not a failure. - List endpoints paginate by cursor: pass
?cursor=from the previous page'snextCursor(limit≤ 100, default 50). - A write that AMENDS an existing record requires
If-Matchcarrying theetagfrom your read. A missing one is 428 (not a silent overwrite); a stale one is 409 with the current record attached, so you can re-plan without a second round trip. Send*to overwrite unconditionally. - Every response carries
ok: true | false. Errors are{ ok: false, code, error, retryable }— branch oncode, never on the Englisherrorsentence. A denial also carriesrequired: { action, kind }, naming the exact scope you are missing. - A DEAD credential is 401 with
WWW-Authenticate(invalid_token,revoked,expired) — stop and get a new token. An in-scope-but-not-permitted call is 403 (read_only,out_of_scope,role_denied) — ask for a broader token, or skip that call. Other statuses: 404 not found · 409 conflict · 429 rate limit. - Writes are exactly-once when you send an idempotency key —
opIdin the body or anIdempotency-Keyheader. A replay returns the ORIGINAL record with 200 andreplayed: true; without a key a retry after a lost response creates a SECOND record, so always send one. - Every write is recorded in the tenant's tamper-evident audit trail against your token. Set
X-Agent-Run-Id,X-Agent-TaskandX-Agent-Modeland they are recorded with it, so the tenant can reconstruct what an automated caller did and why. - Measured quantities always include their ± tolerance band — the band is part of the number; integrations must carry it through, never strip it. Every CONTINUOUS figure carries one: a measurement under
band, each volume and the site rollup underbandskeyed by field. A band states its ownunitand givesrelativeFraction(0.1 = ±10 %, never a percentage). Tallies (regionCount) carry none — a count is not a measured quantity, and we never fabricate a ± for one. - The machine-readable contract is
GET /api/v1/openapi.json(OpenAPI 3.1, generated from this page's source).GET /api/v1/capabilitiesreturns what YOUR token may call, which is narrower.
Errors#
Every failure carries a stable code and a retryable flag. Branch on the code — the English error sentence is for humans and may be reworded.
| Code | HTTP | Retryable |
|---|---|---|
no_credential | 401 | no |
invalid_token | 401 | no |
revoked | 401 | no |
expired | 401 | no |
read_only | 403 | no |
out_of_scope | 403 | no |
role_denied | 403 | no |
invalid_json | 400 | no |
invalid_body | 400 | no |
field_required | 400 | no |
field_invalid | 400 | no |
field_invalid_enum | 400 | no |
field_too_long | 400 | no |
unknown_field | 400 | no |
not_found | 404 | no |
principal_unbound | 409 | no |
conflict | 409 | yes — back off and retry |
confirmation_required | 409 | no |
rate_limited | 429 | yes — back off and retry |
unavailable | 503 | yes — back off and retry |
Endpoints#
88 calls, grouped by what they act on. /api/portal/* is session-only and refuses a bearer token — everything a machine can call is on this page.
Paste an API token and every sample below becomes copyable as a call you can run. It is never rendered, never sent to us, and never stored — the samples show a mask, and only your clipboard gets the real value.
Project records#
Punch items, RFIs, incidents and daily logs — the field-integration write path, with retraction that can itself be undone.
GET /api/v1/projects/:id/recordsRequires read · deliverableProject records of one kind (punch, RFI, incident, daily log), cursor-paginated.
listProjectsRecordsParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
kindquerypunch | rfi | incident | daily-logrequiredWhich record kind to list.
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
includeVoidedquerybooleanInclude RETRACTED records, each carrying `voidedAt`. Without this a voided record is indistinguishable from one that never existed.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundfield_requiredfield_invalid_enumunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects/PROJECT_ID/records?kind=punch" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/projects/:id/recordsRequires create · deliverableCreate a punch, RFI, or incident record — the field-integration write. Send an idempotency key: a replay returns the ORIGINAL record with 200 and `replayed: true`.
createProjectsRecordsParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyone of 3 shapes, by `kind`requiredDiscriminated on `kind`: punch needs `title`; rfi needs `subject` + `question`; incident needs `title`.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_invalid_enumunknown_fieldunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/projects/PROJECT_ID/records" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"kind":"punch","title":"Handrail loose at stair 2","opId":"my-run-001"}'GET /api/v1/projects/:id/records/:recordIdRequires read · deliverableOne project record of ANY kind, including a retracted one (its `voidedAt` says so). The generic read over every `RecordStore` kind.
getProjectsRecordsParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
recordIdpathidrequiredThe record id.
from GET /projects/:id/records → records[].id
kindquerypunch | rfi | incident | daily-log | submittal | drawing | minutesrequiredWhich record kind `recordId` is.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundfield_requiredunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects/PROJECT_ID/records/RECORD_ID?kind=punch" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"PATCH /api/v1/projects/:id/records/:recordIdRequires update · deliverableAmend a record's descriptive fields, for ANY kind. `status` is deliberately NOT patchable — each kind has its own state machine (submittal transitions are server-enforced), and those stay named verbs.
updateProjectsRecordsParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
recordIdpathidrequiredThe record id.
from GET /projects/:id/records → records[].id
If-MatchheaderstringrequiredThe `etag` from your GET of this record. REQUIRED — a missing precondition is 428, not a blind overwrite, because 'I didn't check' and 'I checked' must not be the same request. Send `*` to overwrite unconditionally.
from GET /projects/:id/records/:recordId → etag
bodybodyobject{kind, title, subject, question, description, priority, assignees, dueDate, ballInCourt, involvedParty}required`kind` plus at least one field to amend. Unknown fields are REFUSED, not dropped.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_invalid_enumfield_too_longunknown_fieldconflictunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X PATCH "https://app.updrone.com/api/v1/projects/PROJECT_ID/records/RECORD_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"kind":"punch","priority":"high"}'POST /api/v1/projects/:id/records/:recordId/voidRequires update · deliverableRetract a record. REVERSIBLE — `…/unvoid` takes the retraction back. Idempotent: voiding a voided record does not re-stamp it.
createProjectsRecordsVoidParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
recordIdpathidrequiredThe record id.
from GET /projects/:id/records → records[].id
bodybodyobject{kind}requiredWhich record kind `recordId` is.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsonfield_invalid_enumunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/projects/PROJECT_ID/records/RECORD_ID/void" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"kind":"punch"}'POST /api/v1/projects/:id/records/:recordId/unvoidRequires update · deliverableUn-retract a record — the verb that makes voiding reversible rather than terminal.
createProjectsRecordsUnvoidParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
recordIdpathidrequiredThe record id.
from GET /projects/:id/records?includeVoided=1 → records[].id
bodybodyobject{kind}requiredWhich record kind `recordId` is.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsonfield_invalid_enumunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/projects/PROJECT_ID/records/RECORD_ID/unvoid" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"kind":"punch"}'Deliverables & measurements#
Pull a processed artifact down, and read the measured quantities a scene produced — each figure with the ± band that belongs to it.
GET /api/v1/projects/:id/artifacts/:artifactIdRequires read · deliverableA short-lived, TOKEN-FETCHABLE download link for one artifact. The `href` values in a project's deliverable manifests are portal-session URLs a token cannot use — fetching one returns the login page, not the file.
getProjectsArtifactsParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
artifactIdpathidrequiredThe artifact id.
from GET /projects/:id → project.scenes[].artifacts[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects/PROJECT_ID/artifacts/ARTIFACT_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/projects/:id/quantitiesRequires read · deliverableMeasured quantities from the site snapshot — every measurement with its unit AND ± tolerance band; per-region volumes and the site rollup, each with a `bands` map carrying the ± for every continuous figure (cut/fill/net/footprint/tonnage).
listProjectsQuantitiesParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects/PROJECT_ID/quantities" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"Leads & customers#
Demand before it is a customer, and the customer once it is. Capture a lead, move it through its status machine, convert it into a real record — and read the whole relationship back in one call.
GET /api/v1/leadsRequires read · leadThe tenant's lead inbox, cursor-paginated. `score` is DATA COMPLETENESS (0–85), never a rating of the person.
listLeadsParameters
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
statusqueryNew | Contacted | Qualified | Converted | LostOnly leads in this status.
qquerystring, ≤ 200 charsFree-text over name, email, phone and message — across the whole inbox, not just this page.
ownerIdqueryidOnly leads owned by this member.
from GET /members → members[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalidfield_invalid_enumunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/leads" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/leads/:idRequires read · leadOne lead, with the inbound messages logged against it and the statuses it may move to next — so a status change is planned rather than discovered by 409.
getLeadsParameters
idpathidrequiredThe lead id.
from GET /leads → leads[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/leads/LEAD_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/leadsRequires create · leadCapture a lead. DEDUPED against an open lead with the same email (a second post returns the FIRST lead with `deduped: true`), and matched against an existing customer — the match is a flag, never a merge.
createLeadsParameters
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{name, email, phone, message, source, ownerId, opId}required`name` is required; everything else is what the form actually collected. The server owns the id, the tenant and the timestamp.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_too_longunknown_fieldHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/leads" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"The Bensons","email":"kbenson@example.com","source":"website","opId":"lead-run-001"}'PATCH /api/v1/leads/:idRequires update · leadAmend a lead, including a GUARDED status move. Legal transitions only (`LEAD_TRANSITIONS`); an illegal one is 409 listing what IS legal from here. `Converted` is refused — conversion is its own verb, because it mints a customer.
updateLeadsParameters
idpathidrequiredThe lead id.
from GET /leads → leads[].id
If-MatchheaderstringrequiredThe `etag` from your GET of this record. REQUIRED — a missing precondition is 428, not a blind overwrite. Send `*` to overwrite unconditionally.
from GET /leads/:id → etag
bodybodyobject{status, ownerId, name, email, phone, message}requiredAt least one field. `status` moves through the machine; the rest are plain amendments. Unknown fields are REFUSED, not dropped.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_invalidfield_invalid_enumfield_too_longunknown_fieldconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X PATCH "https://app.updrone.com/api/v1/leads/LEAD_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"status":"Qualified"}'POST /api/v1/leads/:id/qualifyRequires update · leadRead the facts a lead STATED in their own message and return them as a typed persona (buyer / seller / investor). Meters AI credits, so it is a write for rate-limiting purposes. It stores nothing on the lead — what to do with the reading stays a decision you make through PATCH.
createLeadsQualifyParameters
idpathidrequiredThe lead id.
from GET /leads → leads[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/leads/LEAD_ID/qualify" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/leads/:id/convertRequires create · customerConvert a lead into a real CRM customer plus an opening deal, and log the inbound message on the customer's timeline.
createLeadsConvertParameters
idpathidrequiredThe lead id.
from GET /leads → leads[].id
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundconflictunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/leads/LEAD_ID/convert" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/customersRequires read · customerThe tenant's customers, cursor-paginated. `?q=` searches the whole tenant in SQL, so 'no match' means no match rather than 'none on this page'.
listCustomersParameters
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
qquerystring, ≤ 200 charsFree-text over name, email and company, across the tenant.
stagequeryLead | Qualified | Proposal | Active | LostOnly customers in this stage.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalidfield_invalid_enumHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/customers" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/customers/:idRequires read · customerOne customer record with its deals, projects, captures, proposals, invoices and money rollup — the whole relationship in one read.
getCustomersParameters
idpathidrequiredThe customer id.
from GET /customers → customers[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/customers/CUSTOMER_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/customersRequires create · customerCreate a customer. Send an idempotency key: a replay returns the ORIGINAL record with `replayed: true`.
createCustomersParameters
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{name, email, phone, company, stage, source, opId}required`name` is required. `stage` defaults to `Lead`.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_invalid_enumfield_too_longunknown_fieldHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/customers" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Rodriguez Roofing","email":"ops@rodriguez.example","opId":"cust-run-001"}'PATCH /api/v1/customers/:idRequires update · customerAmend a customer's fields. Every change is written to the record's own activity trail, so the customer page shows what an integration did.
updateCustomersParameters
idpathidrequiredThe customer id.
from GET /customers → customers[].id
If-MatchheaderstringrequiredThe `etag` from your GET of this record. REQUIRED — a missing precondition is 428, not a blind overwrite. Send `*` to overwrite unconditionally.
from GET /customers/:id → etag
bodybodyobject{name, email, phone, company, stage, source, ownerId}requiredAt least one field to amend. Unknown fields are REFUSED, not dropped.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_invalidfield_invalid_enumfield_too_longunknown_fieldconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X PATCH "https://app.updrone.com/api/v1/customers/CUSTOMER_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"stage":"Active"}'Deals & pipeline#
The pipeline as data rather than as a board: cents and currency codes, the stage machine's legal triggers stated up front, and moves that refuse illegally rather than silently doing nothing.
GET /api/v1/dealsRequires read · dealThe deal pipeline as DATA — cents and currency, not the board's pre-formatted labels. Cursor-paginated, with the tenant's stage counts and open value alongside.
listDealsParameters
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
stagequerylead | proposal_sent | won | scheduled | captured | delivered | invoiced | lostOnly deals in this stage.
customerIdqueryidOnly this customer's deals.
from GET /customers → customers[].id
openqueryboolean`true` keeps only OPEN deals (everything but `invoiced` and `lost`).
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalidfield_invalid_enumunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/deals" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/deals/:idRequires read · dealOne deal with its activity timeline and the triggers that are LEGAL from its current stage — so you can plan a move without discovering the machine by 409.
getDealsParameters
idpathidrequiredThe deal id.
from GET /deals → deals[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/deals/DEAL_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/dealsRequires create · dealOpen an UNPRICED deal against a customer. The server checks the customer in tenant scope and mints the initial zero value in the tenant's persisted currency; setting contract value is a separate manual-money command, not part of this API intent.
createDealsParameters
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{customerId, contactId, expectedCloseAt, opId}required`customerId` is required and must exist in your tenant. Amount, currency, totals, tax, fees, balances, status and provider fields are not intent and are rejected as unknown fields.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_invalid_enumunknown_fieldunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/deals" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"customerId":"CUSTOMER_ID","opId":"deal-run-001"}'POST /api/v1/deals/:id/advanceRequires update · dealMove a deal through the pipeline machine. An illegal trigger is 409 listing the legal ones — a v1 route never invents a transition. A trigger whose destination the deal is ALREADY in answers 200 with `replayed: true`, so a lost response is safe to retry.
createDealsAdvanceParameters
idpathidrequiredThe deal id.
from GET /deals → deals[].id
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{trigger}requiredThe machine trigger. `lose` is deliberately absent — it takes a reason, so it has its own verb.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalid_enumunknown_fieldconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/deals/DEAL_ID/advance" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"trigger":"win"}'POST /api/v1/deals/:id/loseRequires update · dealClose a deal as lost, with a reason. REVERSIBLE — the pipeline can reopen it. Idempotent for an already-lost deal; a stage with no `lose` edge is refused out loud rather than reported as a close that never happened.
createDealsLoseParameters
idpathidrequiredThe deal id.
from GET /deals → deals[].id
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{reason}requiredWhy it was lost. Free text, stored on the deal and shown on the board.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_too_longunknown_fieldconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/deals/DEAL_ID/lose" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reason":"Went with a cheaper bid"}'Projects#
The unit of delivery work: the list, one project with its scenes and deliverable manifests, and its computed schedule.
GET /api/v1/projectsRequires read · jobList projects (lean summaries: customer, site, status).
listProjectsParameters
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
qquerystring, ≤ 200 charsFree-text search across the TENANT, not just the current page — so 'no match' means no match.
statusquerystringExact project status.
verticalquerystringExact project vertical.
customerIdqueryidOnly this customer's projects.
updatedSincequerydate-timeOnly projects changed since this instant — the cheap change poll.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalidHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/projects/:idRequires read · jobOne project with its scenes (artifacts + export formats) and deliverable manifests (download hrefs).
getProjectsParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects/PROJECT_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/projects/:id/takeoffsRequires read · deliverableEvery takeoff on a project — the quantity documents, each with its rollup by cost code and the provenance mix on every total. A total spanning captured and drawn quantities reports `mixed: true`; a `null` band means no honest ± exists and must not be rendered as zero.
listProjectsTakeoffsParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects/PROJECT_ID/takeoffs" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/projects/:id/takeoffs/:takeoffIdRequires read · deliverableOne takeoff in full: every line with its quantity in canonical SI, the ± it earned (or `null` where none honestly exists), its provenance, and the scope / assumptions / disclosures that make it defensible.
getProjectsTakeoffsParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
takeoffIdpathidrequiredThe takeoff id.
from GET /projects/{id}/takeoffs → takeoffs[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects/PROJECT_ID/takeoffs/TAKEOFF_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/projects/:id/takeoffs/:takeoffId/deltaRequires read · deliverableWhat changed between two revisions of a takeoff — added / removed / changed lines, and for each change WHICH SIDE OF THE ± BAND it sits on. A change inside the band is two readings of the same thing; outside it, something moved.
listProjectsTakeoffsDeltaParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
takeoffIdpathidrequiredThe `to` revision — the newer document.
from GET /projects/{id}/takeoffs → takeoffs[].id
fromqueryidrequiredThe `from` revision to compare against.
from GET /projects/{id}/takeoffs → takeoffs[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundfield_requiredunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects/PROJECT_ID/takeoffs/TAKEOFF_ID/delta?from=VALUE" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/projects/:id/scheduleRequires read · jobThe CPM schedule plan: tasks + computed dates, float, critical path.
listProjectsScheduleParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
startquerydateOverride the project start. A date, NOT an instant — an ISO date-time is rejected. Echoed back in the SAME form it was sent.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundfield_invalidconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects/PROJECT_ID/schedule" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/projectsRequires create · jobOpen a project. Mints the Job, its Site and the linked CRM deal through the real lifecycle core, so a project created here is indistinguishable from one created in the portal.
createProjectsParameters
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{customerId, customerName, name, siteAddress, ownerName, accessNotes, geo, vertical, opId}required`customerId` + `customerName` are required; the server reloads the canonical customer name and persisted tenant currency. Contract value and other authoritative money fields are rejected. Omit `vertical` to take the tenant default.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidunknown_fieldunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/projects" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"customerId":"CUSTOMER_ID","customerName":"Rodriguez Roofing","name":"Warehouse roof scan","siteAddress":"1200 Industrial Way, Austin TX","opId":"proj-run-001"}'PATCH /api/v1/projects/:idRequires update · jobAmend a project's non-financial mutable fields: name, owner, site address and geo, and the trade vertical. Contract value is deliberately not patchable here.
updateProjectsParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
If-MatchheaderstringrequiredThe `etag` from your GET of this record. REQUIRED — a missing precondition is 428, not a blind overwrite. Send `*` to overwrite unconditionally.
from GET /projects/:id → etag
bodybodyobject{name, ownerId, siteAddress, geo, accessNotes, vertical}requiredAt least one field. Authoritative money and status fields are rejected. Changing `vertical` on a project that already has design or delivery content is REFUSED with 409 rather than silently discarding it.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_invalid_enumunknown_fieldconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X PATCH "https://app.updrone.com/api/v1/projects/PROJECT_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Warehouse roof scan — phase 2"}'POST /api/v1/projects/:id/lifecycleRequires update · jobMove a project through `JOB_MACHINE`. An illegal trigger is 409 listing the legal ones; a trigger whose destination the project is ALREADY in answers 200 with `replayed: true`.
createProjectsLifecycleParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{trigger}required`cancel` is deliberately absent — cancelling is irreversible through this API and is not an agent verb. `deliverable_shipped` is refused while the project has no packaged deliverable, so the stage can never contradict the count.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalid_enumunknown_fieldconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/projects/PROJECT_ID/lifecycle" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"trigger":"schedule_assigned"}'POST /api/v1/projects/:id/capture/presignRequires create · captureBegin a server-owned capture session and receive short-lived, content-type-pinned PUT URLs. The SERVER mints every object key inside your tenant's namespace and records them as the session's expected set — you cannot choose, influence or add a key.
createProjectsCapturePresignParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{source, device, capturedBy, frames, depth, confidence, control, hasLidar, mode, engine, quality, idempotencyKey, opId}required`frames` is a MANIFEST — one entry per file you intend to upload, carrying its content type and declared size. The declared sizes are an advisory fast-fail; the real bytes are measured at finalize.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_invalid_enumunknown_fieldconflictunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/projects/PROJECT_ID/capture/presign" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"source":"drone","device":"Mavic 3E","frames":[{"contentType":"image/jpeg","size":4200000}],"opId":"cap-run-001"}'POST /api/v1/projects/:id/capture/:sessionId/finalizeRequires update · captureComplete a presigned capture and start the reconstruction. SERVER-AUTHORITATIVE and BODY-FREE: the server HEAD-verifies its own expected keys, meters the real bytes, and kicks the pipeline. Idempotent — a duplicate call re-meters nothing.
createProjectsCaptureFinalizeParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
sessionIdpathidrequiredThe capture session id.
from POST /projects/:id/capture/presign → sessionId
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundfield_requiredconflictunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/projects/PROJECT_ID/capture/SESSION_ID/finalize" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/projects/:id/capture/:sessionId/abortRequires update · captureReport that an upload failed or was cancelled, so the session stops claiming to be in flight. Only touches a session still `uploading`; a racing finalize always wins.
createProjectsCaptureAbortParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
sessionIdpathidrequiredThe capture session id.
from POST /projects/:id/capture/presign → sessionId
reasonqueryfailed | cancelled`failed` (the network gave up) is never charged. `cancelled` (you chose to stop) bills the flat, non-refundable UPLOAD FEE — the same rule a person sees in the cancel dialog. Defaults to `failed`.
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundfield_invalid_enumHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/projects/PROJECT_ID/capture/SESSION_ID/abort" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/projects/:id/processRequires update · captureRe-run the reconstruction for a capture. METERED COMPUTE — budgeted, and gated: an infeasible engine/mode is refused before anything is queued, and a capture already in flight is never started twice.
createProjectsProcessParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{captureId, mode, engine, quality, structureSpanM, sceneTag}Everything is optional: no body reprocesses the newest capture on the project's own persisted mode and engine. An explicit `mode`/`engine` also PERSISTS on the project, so the record, the charge and the backend agree.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_invalidfield_invalid_enumunknown_fieldconflictunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/projects/PROJECT_ID/process" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"engine":"native","mode":"cloud"}'GET /api/v1/projects/:id/complianceRequires read · deliverableWhat this job owes: the STATUTORY obligations the site's jurisdiction imposes (with citations), and — when a solar design exists — whether its plan set is ready to submit.
listProjectsComplianceParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects/PROJECT_ID/compliance" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/projects/:id/permitRequires read · deliverableThe permit metadata in effect for this project — the tenant defaults with any per-project override merged over them.
listProjectsPermitParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects/PROJECT_ID/permit" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/projects/:id/permitRequires update · deliverableSet this project's permit override. Tenant-level defaults stay untouched — this is the per-job exception, not a settings edit.
createProjectsPermitParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
bodybodyobject{}requiredSparse permit metadata — unknown keys are dropped by the store's validator.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/projects/PROJECT_ID/permit" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"ahjName":"City of Austin"}'GET /api/v1/projects/:id/export-formatsRequires read · deliverableWhich formats this project's scenes can actually be exported to. A codec that is not installed is ABSENT rather than listed-and-then-refused, so a caller plans an export it can complete.
listProjectsExport-formatsParameters
idpathidrequiredThe project id.
from GET /projects → projects[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/projects/PROJECT_ID/export-formats" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"Client pages & exports#
Getting work out of Updrone. Client pages are assembled rather than stored — every project and customer always has one — and a tenant export is queued, swept and polled, because archiving a whole tenant is not something to hold a socket open for.
GET /api/v1/client-pagesRequires read · deliverableThe tenant's client pages — one per project and customer, always. ASSEMBLED on read, not stored, so there is nothing to create; the capability token in each link is minted server-side and never accepted from a caller.
listClient-pagesParameters
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
kindqueryproject | customerOnly project pages, or only customer pages.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalidfield_invalid_enumHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/client-pages" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/exportsRequires create · settingsRequest a full tenant data export. Queued, not performed: `cron/exports` packages it and you poll `GET /exports/:id`. METERED COMPUTE — this reads and archives the whole tenant.
createExportsParameters
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{opId}No parameters — an export is the whole tenant. Send a key: a retried request must not queue a second archive of everything.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedinvalid_jsoninvalid_bodyunknown_fieldconflictunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/exports" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"opId":"exp-run-001"}'GET /api/v1/exportsRequires read · settingsYour export requests, newest first, with their status and — once ready — the archive's size and expiry.
listExportsParameters
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalidHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/exports" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/exports/:idRequires read · settingsOne export — THE POLL TARGET. `queued → tables → assets → packaging → ready`, with `error` naming the phase that failed.
getExportsParameters
idpathidrequiredThe export id.
from POST /exports → export.id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/exports/EXPORT_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"Saved reports#
What a tenant has chosen to keep watching. A report is config-backed — it stores the figures and the queries, not their values, and the board is re-derived every time it is opened — so these calls describe what a report IS, and the numbers stay where they are computed. Reports are private to the member who saved them, which is why an unbound token is refused rather than shown an empty shelf.
GET /api/v1/reportsRequires read · reportSaved reports you can see, newest first. A report is PRIVATE TO THE MEMBER WHO SAVED IT — a Manager token sees the tenant's, a Staff token sees the bound member's own, and an unbound token is refused rather than shown an empty list.
listReportsParameters
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedprincipal_unboundfield_invalidHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/reports" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/reports/:idRequires read · reportOne saved report's DEFINITION — the figures it watches and the queries behind them, not their current values. The board is re-derived when it is opened; nothing is computed by this read.
getReportsParameters
idpathidrequiredThe report id.
from GET /reports → reports[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedprincipal_unboundnot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/reports/REPORT_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"Invoices#
Asking to be paid. A draft asks nobody for anything; issuing one puts a live payment page in front of a customer, so it is confirmed. Refunds and payment itself are not here — returning money and paying are human acts.
GET /api/v1/invoicesRequires read · paymentThe tenant's invoices, cursor-paginated. Amounts in cents; status straight from `INVOICE_MACHINE`.
listInvoicesParameters
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
statusquerydraft | sent | paid | overdue | void | refundedOnly invoices in this status.
customerIdqueryidOnly this customer's invoices.
from GET /customers → customers[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalidfield_invalid_enumHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/invoices" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/invoices/:idRequires read · paymentOne invoice with its line items and the legal triggers from here. Payment details are summarised, never the raw processor payload.
getInvoicesParameters
idpathidrequiredThe invoice id.
from GET /invoices → invoices[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/invoices/INVOICE_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/invoicesRequires create · paymentCreate a server-priced DRAFT invoice from one active tenant pricing package and an optimistic pricebook version. The API accepts selectors and quantity-free package intent, never line prices or totals.
createInvoicesParameters
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{customerId, invoiceType, terms, packageId, sourceVersion, opId}required`packageId` must name an active package in this tenant; `sourceVersion` is the opaque current pricebook version. Send `opId` or `Idempotency-Key`. Currency, lines, unit prices, totals, tax, fees, balances, status, provider ids and URLs are rejected as unknown fields.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_invalid_enumunknown_fieldconflictunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/invoices" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"customerId":"CUSTOMER_ID","packageId":"PACKAGE_ID","sourceVersion":"PRICEBOOK_VERSION","opId":"inv-run-001"}'POST /api/v1/invoices/:id/issueRequires update · paymentIssue a draft invoice: `draft → sent` in `INVOICE_MACHINE`, and the hosted payment page goes LIVE. Two-phase confirm — this is the moment you ask a customer for money.
createInvoicesIssueParameters
idpathidrequiredThe invoice id.
from GET /invoices → invoices[].id
X-Agent-Confirmed-Byheaderstring, ≤ 500 charsWho authorised it; recorded as a claim, not proof.
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundconflictconfirmation_requiredHighlighted codes are retryable. Branch on code, never on the English message.
Example request
# This call is irreversible. The FIRST request returns 409 with a confirmToken and a
# willDo description; re-send it with x-updrone-confirm: <token> to perform it.
curl -X POST "https://app.updrone.com/api/v1/invoices/INVOICE_ID/issue" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/invoices/:id/voidRequires update · paymentVoid an invoice. TERMINAL in `INVOICE_MACHINE`. A `paid` invoice cannot be voided — returning money is a refund, which this API does not do.
createInvoicesVoidParameters
idpathidrequiredThe invoice id.
from GET /invoices → invoices[].id
X-Agent-Confirmed-Byheaderstring, ≤ 500 charsWho authorised it; recorded as a claim.
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundconflictconfirmation_requiredHighlighted codes are retryable. Branch on code, never on the English message.
Example request
# This call is irreversible. The FIRST request returns 409 with a confirmToken and a
# willDo description; re-send it with x-updrone-confirm: <token> to perform it.
curl -X POST "https://app.updrone.com/api/v1/invoices/INVOICE_ID/void" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/invoices/:id/pdfRequires read · paymentA short-lived, TOKEN-FETCHABLE link to the invoice PDF. The hosted page's own URL needs a session a token does not have.
listInvoicesPdfParameters
idpathidrequiredThe invoice id.
from GET /invoices → invoices[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/invoices/INVOICE_ID/pdf" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"Proposals#
Winning the work. Drafts are inert and freely editable; sending one is irreversible, confirmed and budgeted, because it puts a document in front of a named human who can act on it.
GET /api/v1/proposalsRequires read · proposalThe tenant's proposals, cursor-paginated. Operator-shaped: status, recipient, totals and the machine's legal next moves.
listProposalsParameters
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
statusquerydraft | sent | viewed | accepted | declined | expired | voidedOnly proposals in this status.
customerIdqueryidOnly this customer's proposals.
from GET /customers → customers[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalidfield_invalid_enumHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/proposals" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/proposals/:idRequires read · proposalOne proposal with its line items and the legal triggers from here. NEVER carries the signing OTP or the signed PDF bytes.
getProposalsParameters
idpathidrequiredThe proposal id.
from GET /proposals → proposals[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/proposals/PROPOSAL_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/proposalsRequires create · proposalCreate a DRAFT proposal priced by the server from the tenant's persisted catalog/rate settings and currency. A draft is inert; sending remains a separate confirmed act.
createProposalsParameters
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{customerId, title, projectId, opId}requiredThe customer must exist in your tenant. When `projectId` is present it must belong to that customer and supplies the server-owned vertical/site/deal pricing context. Client line prices, currency, totals, tax, fees, balances, status and provider/URL fields are rejected.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_invalid_enumunknown_fieldconflictunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/proposals" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"customerId":"CUSTOMER_ID","title":"Roof scan + solar","opId":"prop-run-001"}'PATCH /api/v1/proposals/:idRequires update · proposalAmend a DRAFT's non-financial presentation intent: title, recipient, or embedded 3D scene. Priced lines and authoritative economics are not patchable through this command.
updateProposalsParameters
idpathidrequiredThe proposal id.
from GET /proposals → proposals[].id
If-MatchheaderstringrequiredThe `etag` from your GET of this record. REQUIRED — a missing precondition is 428, not a blind overwrite. Send `*` to overwrite unconditionally.
from GET /proposals/:id → etag
bodybodyobject{title, recipientEmail, recipientName, sceneId}requiredAt least one field. Money, tax, fee, balance, status, provider and URL fields are rejected. A proposal that is no longer a draft is 409 with its current status.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_too_longunknown_fieldconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X PATCH "https://app.updrone.com/api/v1/proposals/PROPOSAL_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Revised scope"}'POST /api/v1/proposals/:id/sendRequires update · proposalSend the proposal to its recipient. IRREVERSIBLE — there is no un-send. Two-phase confirm; the `willDo` names the recipient (redacted). Freezes the figures AS SENT, so a later design change cannot retroactively alter what the customer saw.
createProposalsSendParameters
idpathidrequiredThe proposal id.
from GET /proposals → proposals[].id
X-Agent-Confirmed-Byheaderstring, ≤ 500 charsWho authorised the send. SELF-REPORTED and recorded as a claim, not proof — the server-issued confirm token is the control.
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundconflictconfirmation_requiredunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
# This call is irreversible. The FIRST request returns 409 with a confirmToken and a
# willDo description; re-send it with x-updrone-confirm: <token> to perform it.
curl -X POST "https://app.updrone.com/api/v1/proposals/PROPOSAL_ID/send" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/proposals/:id/voidRequires update · proposalVoid a proposal. TERMINAL in `PROPOSAL_MACHINE` — there is no way back, and a voided proposal's hosted page stops working. Two-phase confirm.
createProposalsVoidParameters
idpathidrequiredThe proposal id.
from GET /proposals → proposals[].id
X-Agent-Confirmed-Byheaderstring, ≤ 500 charsWho authorised it; recorded as a claim.
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundconflictconfirmation_requiredHighlighted codes are retryable. Branch on code, never on the English message.
Example request
# This call is irreversible. The FIRST request returns 409 with a confirmToken and a
# willDo description; re-send it with x-updrone-confirm: <token> to perform it.
curl -X POST "https://app.updrone.com/api/v1/proposals/PROPOSAL_ID/void" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/proposals/:id/reviseRequires create · proposalFork a sent proposal into a NEW draft version. The original stays exactly as the customer saw it — revising never edits a document somebody has already been shown.
createProposalsReviseParameters
idpathidrequiredThe proposal id.
from GET /proposals → proposals[].id
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/proposals/PROPOSAL_ID/revise" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/proposals/:id/duplicateRequires create · proposalCopy a proposal into a fresh draft — the template path, for a similar job on a different customer.
createProposalsDuplicateParameters
idpathidrequiredThe proposal id.
from GET /proposals → proposals[].id
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/proposals/PROPOSAL_ID/duplicate" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"Scenes & geometry#
The reconstructions themselves: what is ready, what each one produced, and the plane-detection kernel that turns a dense point cloud into a roof you can design on.
GET /api/v1/scenesRequires read · sceneThe tenant's reconstructed scenes. Ready scenes carry their artifacts and export formats; pending ones carry only an id and a label, because there is nothing yet to describe.
listScenesParameters
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
projectIdqueryidOnly this project's scenes.
from GET /projects → projects[].id
statusqueryready | pending`ready` (reconstructed) or `pending` (still processing). Default: both.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalidfield_invalid_enumHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/scenes" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/scenes/:idRequires read · sceneOne ready scene with its artifacts, export formats and the geometry URLs a viewer needs.
getScenesParameters
idpathidrequiredThe scene id.
from GET /scenes → scenes[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/scenes/SCENE_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/scenes/:id/detect-roofRequires read · sceneRun the plane-detection kernel over a scene's dense reconstruction and report the roof it finds. PERSISTS NOTHING — it is a reading, and it gates on `read` because of that.
createScenesDetect-roofParameters
idpathidrequiredThe scene id.
from GET /scenes → scenes[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/scenes/SCENE_ID/detect-roof" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/scenes/:id/geometryRequires read · sceneA short-lived, TOKEN-FETCHABLE link to one of a scene's geometry artifacts: the mesh, the bare-earth DTM, the plan-set model, or the solar surface.
listScenesGeometryParameters
idpathidrequiredThe scene id.
from GET /scenes → scenes[].id
kindquerymesh | dtm | planset-model | solarrequiredWhich artifact. A scene that never produced this one answers 404 — it is genuinely absent, not withheld.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundfield_requiredfield_invalid_enumunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/scenes/SCENE_ID/geometry?kind=mesh" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/scenes/:id/measurementsRequires read · sceneThe measurements taken on a scene. Every continuous figure carries its ± tolerance BAND — the band is part of the number, and an integration must carry it through rather than strip it.
listScenesMeasurementsParameters
idpathidrequiredThe scene id.
from GET /scenes → scenes[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/scenes/SCENE_ID/measurements" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/scenes/:id/measurementsRequires create · deliverableTake a measurement on a scene. The kernel computes the value from your points and REJECTS a bad pick set (too few points, a non-finite coordinate) rather than storing it.
createScenesMeasurementsParameters
idpathidrequiredThe scene id.
from GET /scenes → scenes[].id
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{kind, points, label, opId}required`points` are in the scene's own frame — see `GET /projects/:id/quantities → frame` for its EPSG and origin. The accuracy tier is DERIVED from the scene and is never something you set.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_invalid_enumunknown_fieldconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/scenes/SCENE_ID/measurements" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"kind":"distance","points":[{"x":0,"y":0,"z":0},{"x":3,"y":0,"z":0}],"opId":"meas-run-001"}'DELETE /api/v1/scenes/:id/measurementsRequires update · deliverableRemove one measurement. Gates on `update`, not `delete`: it retracts an annotation on a scene, it does not destroy the scene.
deleteScenesMeasurementsParameters
idpathidrequiredThe scene id.
from GET /scenes → scenes[].id
measurementIdqueryidrequiredWhich measurement to remove.
from GET /scenes/:id/measurements → measurements[].id
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundfield_requiredHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X DELETE "https://app.updrone.com/api/v1/scenes/SCENE_ID/measurements?measurementId=VALUE" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/scenes/:id/waypointsRequires read · sceneThe scene's navigation graph — the pointers, go-tos and tour stops authored on it.
listScenesWaypointsParameters
idpathidrequiredThe scene id.
from GET /scenes → scenes[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/scenes/SCENE_ID/waypoints" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/scenes/:id/waypointsRequires create · deliverableSave a waypoint — a scene-space position, a kind and a label. These are the stops a client tour plays.
createScenesWaypointsParameters
idpathidrequiredThe scene id.
from GET /scenes → scenes[].id
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{kind, label, position, link, order, opId}required`position` is `[x, y, z]` in the scene's own frame — see `GET /projects/:id/quantities → frame` for its EPSG and origin. A waypoint is navigation metadata: it never asserts a flight is permitted and never raises the scene's accuracy tier.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidunknown_fieldHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/scenes/SCENE_ID/waypoints" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"kind":"tour-stop","label":"North elevation","position":[0,12,30],"opId":"wp-run-001"}'Capture & processing#
Getting pixels in and turning them into a scene: the presigned upload handshake where the server owns every key, the reconstruction kick, and the poll target that says which stage a run is on and why it stopped.
GET /api/v1/capturesRequires read · captureEvery capture session in the tenant, newest first, with its per-stage progress. This is what you poll while a reconstruction runs.
listCapturesParameters
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
projectIdqueryidOnly this project's captures.
from GET /projects → projects[].id
statusquerycapturing | uploading | queued | processing | ready | failedOnly captures in this status.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalidfield_invalid_enumHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/captures" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/captures/:idRequires read · captureOne capture session — THE PROCESSING-STATUS POLL TARGET. Carries the per-stage breakdown and, on a failure, the stage that failed and why.
getCapturesParameters
idpathidrequiredThe capture session id.
from GET /captures → captures[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/captures/CAPTURE_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/uploads/initRequires create · captureIssue an opaque, tenant/principal/project-bound upload intent. The server reserves quota and mints every key; callers declare only MIME and a byte ceiling, never a tenant, key, filename or URL.
createUploadsInitParameters
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{projectId, captureId, uploads}required`captureId` is required for solar artifacts and forbidden for project attachments. `maxBytes` is an upload ceiling and quota reservation, not a claimed landed size. Completion HEAD-verifies the provider's actual key, MIME and bytes from the server-side intent row.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_invalid_enumunknown_fieldconflictunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/uploads/init" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"projectId":"PROJECT_ID","uploads":[{"purpose":"project_attachment","contentType":"image/jpeg","maxBytes":12000000}]}'POST /api/v1/uploads/completeRequires update · captureAtomically consume one opaque upload intent. The server loads its own exact keys, HEAD-verifies MIME and real bytes, meters quota, and returns opaque artifact references; callers cannot submit a key or URL.
createUploadsCompleteParameters
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{intent}requiredOnly the opaque token from `/uploads/init`. Tenant, principal, project, keys, MIME and byte ceilings are reloaded from the durable server row.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidunknown_fieldconflictunavailableHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/uploads/complete" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"intent":"uit_OPAQUE_TOKEN"}'Templates & geocoding#
The pieces a project is assembled from: the document templates every invoice, proposal and client page renders through, and the address lookup that turns a street line into the coordinate every downstream kernel needs.
GET /api/v1/geocodeRequires read · jobAddress ⇄ coordinate, both directions. `?q=` for forward suggestions, `?lat=&lng=` for the address at a point. UNITED STATES ONLY today.
listGeocodeParameters
qquerystring, ≤ 300 charsForward: the partial address to resolve. Mutually exclusive with lat/lng.
latquerynumberReverse: latitude. Send with `lng`.
lngquerynumberReverse: longitude. Send with `lat`.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_requiredfield_invalidHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/geocode" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/templatesRequires read · templatesThe tenant's DOCUMENT templates — invoice, proposal, client page and report — plus the assignment map that decides which one a given customer or project gets.
listTemplatesParameters
kindqueryinvoice | proposal | clientPage | reportOnly templates of this kind.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalid_enumHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/templates" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/templates/:idRequires read · templatesOne template, with its document body — the blocks a rendered invoice, proposal, client page or report is built from.
getTemplatesParameters
idpathidrequiredThe template id.
from GET /templates → templates[].id
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/templates/TEMPLATE_ID" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"PUT /api/v1/templates/assignmentsRequires update · templatesPoint a template at a scope: the tenant default, one customer, or one project. PUT because it is a whole-value replace — sending the same assignment twice leaves exactly one pointer.
setTemplatesAssignmentsParameters
bodybodyobject{kind, scope, scopeId, templateId}requiredOmit `templateId` to CLEAR an override, so the scope falls back to the next one up (project → customer → tenant → system).
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitednot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_invalid_enumunknown_fieldHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X PUT "https://app.updrone.com/api/v1/templates/assignments" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"kind":"proposal","scope":"tenant","scopeId":"","templateId":"TEMPLATE_ID"}'Discovery & sync#
Find things, and learn what moved since you last looked — the calls that replace re-reading every collection and diffing it.
GET /api/v1/changesRequires read · jobWhat changed since an instant — the poll that replaces re-reading every collection and diffing it. Pass the returned `cursor` back as `since`. `hasDeletions: false` is stated explicitly: this feed is derived from timestamps and cannot see a removal, so do not infer completeness.
listChangesParameters
sincequerydate-timerequiredReturn changes strictly after this instant. Use the previous response's `cursor`.
from GET /changes → cursor
kindsquerystring, ≤ 200 charsComma-separated families to watch: project, lead, customer, deal, capture. Absent ⇒ everything your token may read, which is the safer default. `coverage[]` in the response says what was actually looked at.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_requiredfield_invalidHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/changes?since=2026-07-01T00%3A00%3A00Z" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/searchRequires read · jobCross-entity search over everything your token can read. Each hit is gated by the SAME per-surface grant check the portal uses, so a token scoped away from customers gets projects and no customers from one query.
listSearchParameters
qquerystring, ≤ 200 charsrequiredThe search text.
typesquerystringComma-separated hit types to keep.
limitqueryinteger, 1–50, default 20Max hits (≤ 50). The applied value is echoed.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_requiredfield_invalidHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/search?q=VALUE" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/membersRequires read · staffThe tenant's active roster — the ids `assignees` accepts. Writing an unknown assignee is a 400, so this is how you discover a valid one. Emails are not returned.
listMembersReturns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/members" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"Token & capabilities#
Introspection. Ask what your own credential is and what it may do, before you act rather than after a 403.
GET /api/v1/meRequires read · settingsIntrospect YOUR token: its scope, expiry, member binding, the grant grid it can actually perform, and how much rate-limit budget is left. Cheap and side-effect-free — call it at the start of a run instead of probing destructively.
listMeReturns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/me" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"GET /api/v1/capabilitiesRequires read · settingsThe agent's bootstrap call: which endpoints YOUR token may call (and why not, when it may not), plus the tenant's configured verticals and modules — so you read the endpoint list through the right lens rather than as a flat menu.
listCapabilitiesReturns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/capabilities" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"Bookings#
Requests customers have made, and the two acts that answer them. A `requested` row HOLDS its slot against every other customer until you do — so this is work, not history. Confirming re-checks the window and refuses a clash with alternatives rather than double-booking; declining carries a reason the customer reads. There is deliberately no endpoint that confirms a booking on a rule or a schedule: confirmation is a human act or the tenant's own recorded instant-book policy, and nothing else.
GET /api/v1/booking_requestsRequires read · bookingBooking requests, newest first. A `requested` row is HOLDING its slot against every other customer until somebody answers it, so this list is work rather than history — `expiresAt` says how long the hold has left.
listBooking_requestsParameters
statusqueryrequested | confirmed | declined | withdrawn | expiredNarrow to one status. Omitted ⇒ all.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalidHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/booking_requests" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/booking_requests/confirmRequires update · bookingConfirm a booking request: materializes the calendar visit, attaches or creates the customer, advances the deal, queues the confirmation and reminders. Re-checks the window first — a request that has been sitting in an inbox may have been overtaken, and a clash answers 409 `slot_conflict` with what is in the way AND three open times. Nothing here resolves a clash for you: only an explicit `override.reason` proceeds, and it is recorded.
createBooking_requestsConfirmParameters
bodybodyobject{id, title, override}required`id` is the request to confirm. `override.reason` is the ONLY way past a clash, and it is recorded — an override with no stated cause is indistinguishable from the check never having run.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_requiredfield_invalidnot_foundconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/booking_requests/confirm" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":"bkr_…"}'POST /api/v1/booking_requests/declineRequires update · bookingDecline a booking request WITH a reason, and get back the nearest open times. The reason reaches the customer — 'we can't do that time' with nothing after it is how a business loses somebody who was ready to buy over a clash a different day would have solved. Declining frees the hold immediately.
createBooking_requestsDeclineParameters
bodybodyobject{id, reason}required`reason` reaches the customer. Omitting it is allowed and produces a bare refusal — which is the thing this endpoint exists to make avoidable.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_requirednot_foundconflictHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/booking_requests/decline" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":"bkr_…","reason":"We are on another site that morning."}'The day#
What a given day holds and how prepared it is: each visit's site, the age of its imagery, whether a roof is marked, whether a draft flight plan exists, and whether a human has reviewed the drafts. `readiness` describes the PREPARATION and never the flight — there is no go/no-go here, and nothing in this product authorizes one.
GET /api/v1/day_sheetRequires read · bookingA day's visits with their day-of preparation: site, imagery age, roof mark, draft flight plan, checklist. Scoped to the token's bound member by default; `all=1` widens to the whole team for an Owner/Manager token. Deliberately carries NO go/no-go, readiness score, or 'ready to fly' — `readiness` describes the PREPARATION (whether the steps ran and whether a human reviewed the drafts), never the flight. Nothing in this product authorizes one.
listDay_sheetParameters
datequerystringYYYY-MM-DD in the TENANT's timezone. Omitted ⇒ the tenant's today, which is not necessarily the caller's.
allquerystring`1` widens from the bound member's day to the whole team. Ignored for a token that is not Owner/Manager.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedfield_invalidHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/day_sheet" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"Tasks#
Operational tasks for the member a token is bound to — read them, and file new ones. Both verbs need that binding: a task owned by a credential rather than by a person is one no human would ever be shown.
GET /api/v1/tasksRequires read · bookingOperational tasks for the token's tenant. This read is scoped to a MEMBER (owned + assigned), so the token must be bound to one — an unbound token gets 409 `principal_unbound` rather than a misleading empty list.
listTasksParameters
cursorquerycursorResume after the previous page's nextCursor.
limitqueryinteger, 1–100, default 50Page size. Values outside the range are CLAMPED, and the response tells you so.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedprincipal_unboundfield_invalidHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X GET "https://app.updrone.com/api/v1/tasks" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN"POST /api/v1/tasksRequires create · bookingFile an operational task. It is OWNED by the member the token is bound to — so an unbound token is refused with 409 `principal_unbound` rather than filing work against a synthetic principal, where the task would exist, cost nothing, and be visible to nobody.
createTasksParameters
Idempotency-Keyheaderstring, ≤ 64 charsEquivalent to `opId` in the body; send one on every retry-able write.
X-Agent-Run-Idheaderstring, ≤ 500 charsRecorded in the tenant's audit trail alongside the write.
X-Agent-Taskheaderstring, ≤ 500 charsWhat the run was asked to do; recorded with the write.
bodybodyobject{title, details, list, due, time, starred, projectId, captureId, opId}required`title` is required. `due` is a civil date (`YYYY-MM-DD`) and `time` a 24-hour wall clock (`HH:MM`) — an unscheduled task carries neither, and a `time` without a `due` does not schedule anything. `list` files it under a personal bucket, created on first use. `projectId` must name a project this token can reach; an unreachable one is 404, exactly as another tenant's would be. The server owns the id, the owner and the timestamp.
Returns
Solid = always present. Dashed = present when applicable.
Errors
no_credentialinvalid_tokenrevokedexpiredread_onlyout_of_scoperole_deniedrate_limitedprincipal_unboundnot_foundinvalid_jsoninvalid_bodyfield_requiredfield_invalidfield_too_longunknown_fieldHighlighted codes are retryable. Branch on code, never on the English message.
Example request
curl -X POST "https://app.updrone.com/api/v1/tasks" \
-H "Authorization: Bearer sk_YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Re-fly the north roof plane","due":"2026-08-03","opId":"task-run-001"}'