Developers
CrewFlow Public API
Version 1.0.0
Key-authenticated read and write access to your CrewFlow organisation's data. Every request is scoped to the organisation that owns the API key; there is NO cross-organisation access. Read and write are DISTINCT scopes — a key must hold the matching write scope (e.g. write:customers) to create or update, even if it can already read. Writes validate their body strictly (unknown fields are rejected), pin every new row to the key's organisation, compute money server-side, and return the created/updated record. Updates via PATCH are idempotent (re-sending the same body yields the same state); a repeated create (POST) makes a new record. VERSIONING: the version is in the URL path (/api/v1). Breaking changes ship under a new prefix (/api/v2) while /api/v1 keeps working; additive changes (new optional fields, new endpoints) are made in place.
Base URL: https://app.crewflow.uk/api/v1 · Machine-readable spec: openapi.json
Authentication
Send your API key as `Authorization: Bearer crewflow_sk_...`. Mint and scope keys in Settings → API keys. Keys can be revoked at any time and take effect on the next request.
Authorization: Bearer crewflow_sk_...Scopes
Every key is granted a specific set of scopes. Read and write are distinct: a key needs the matching write scope to create or update, even if it can already read that resource.
| Scope | Grants |
|---|---|
| read:jobs | Read jobs |
| read:customers | Read customers |
| read:invoices | Read invoices |
| read:quotes | Read quotes |
| read:time | Read time entries |
| read:staff | Read staff roster |
| read:expenses | Read expenses |
| read:materials | Read material requests |
| write:customers | Create & update customers |
| write:leads | Create leads |
| write:jobs | Create & update jobs |
| write:quotes | Create quotes |
| write:expenses | Create & update expenses |
| write:invoices | Update invoice status & metadata |
Rate limits
Requests are limited to 120 per minute per key(shared across every v1 endpoint, keyed by the API key, not by IP). Over budget returns 429.
Endpoints
Identity
/meany valid keyThe identity the API key carries.
Example
curl https://app.crewflow.uk/api/v1/me \
-H "Authorization: Bearer crewflow_sk_..."Jobs
/jobsread:jobsList jobs.
Example
curl https://app.crewflow.uk/api/v1/jobs \
-H "Authorization: Bearer crewflow_sk_..."/jobswrite:jobsCreate a job.
Request body
| Field | Type | Required |
|---|---|---|
| status | "new" | "in-progress" | "completed" | "blocked" | "cancelled" | No |
| scheduled_date | string | No |
| customer_id | string | No |
| notes | string | No |
| site_address_line1 | string | No |
| site_address_line2 | string | No |
| site_city | string | No |
| site_county | string | No |
| site_postcode | string | No |
| site_country | string | No |
Example
curl -X POST https://app.crewflow.uk/api/v1/jobs \
-H "Authorization: Bearer crewflow_sk_..." \
-H "Content-Type: application/json" \
-d '{}'/jobs/{id}read:jobsFetch a single job by id.
Example
curl https://app.crewflow.uk/api/v1/jobs/{id} \
-H "Authorization: Bearer crewflow_sk_..."/jobs/{id}write:jobsUpdate a job.
Request body
| Field | Type | Required |
|---|---|---|
| status | "new" | "in-progress" | "completed" | "blocked" | "cancelled" | No |
| scheduled_date | string | No |
| customer_id | string | No |
| notes | string | No |
| site_address_line1 | string | No |
| site_address_line2 | string | No |
| site_city | string | No |
| site_county | string | No |
| site_postcode | string | No |
| site_country | string | No |
At least one field must be provided. Omitted fields are left unchanged.
Example
curl -X PATCH https://app.crewflow.uk/api/v1/jobs/{id} \
-H "Authorization: Bearer crewflow_sk_..." \
-H "Content-Type: application/json" \
-d '{}'Customers
/customersread:customersList customers.
Example
curl https://app.crewflow.uk/api/v1/customers \
-H "Authorization: Bearer crewflow_sk_..."/customerswrite:customersCreate a customer.
Request body
| Field | Type | Required |
|---|---|---|
| name | string | Yes |
| string | No | |
| phone | string | No |
| address_line1 | string | No |
| address_line2 | string | No |
| city | string | No |
| county | string | No |
| postcode | string | No |
| country | string | No |
| notes | string | No |
Example
curl -X POST https://app.crewflow.uk/api/v1/customers \
-H "Authorization: Bearer crewflow_sk_..." \
-H "Content-Type: application/json" \
-d '{"name":"..."}'/customers/{id}read:customersFetch a single customer by id.
Example
curl https://app.crewflow.uk/api/v1/customers/{id} \
-H "Authorization: Bearer crewflow_sk_..."/customers/{id}write:customersUpdate a customer.
Request body
| Field | Type | Required |
|---|---|---|
| name | string | No |
| string | No | |
| phone | string | No |
| address_line1 | string | No |
| address_line2 | string | No |
| city | string | No |
| county | string | No |
| postcode | string | No |
| country | string | No |
| notes | string | No |
At least one field must be provided. Omitted fields are left unchanged.
Example
curl -X PATCH https://app.crewflow.uk/api/v1/customers/{id} \
-H "Authorization: Bearer crewflow_sk_..." \
-H "Content-Type: application/json" \
-d '{}'Leads
/leadswrite:leadsCapture a lead.
Request body
| Field | Type | Required |
|---|---|---|
| contact_name | string | Yes |
| contact_email | string | No |
| contact_phone | string | No |
| source | "phone" | "web" | "referral" | "walk_in" | "social" | "repeat" | "portal" | "other" | Yes |
| service | string | No |
| urgency | "low" | "normal" | "high" | "urgent" | No |
| postcode | string | No |
| estimated_value | number | No |
| notes | string | No |
| customer_id | string | No |
At least one of contact_email or contact_phone is required.
Example
curl -X POST https://app.crewflow.uk/api/v1/leads \
-H "Authorization: Bearer crewflow_sk_..." \
-H "Content-Type: application/json" \
-d '{"contact_name":"...","source":"phone"}'Invoices
/invoicesread:invoicesList invoices.
Example
curl https://app.crewflow.uk/api/v1/invoices \
-H "Authorization: Bearer crewflow_sk_..."/invoices/{id}read:invoicesFetch a single invoice by id.
Example
curl https://app.crewflow.uk/api/v1/invoices/{id} \
-H "Authorization: Bearer crewflow_sk_..."/invoices/{id}write:invoicesUpdate an invoice's status and metadata (never its money).
Request body
| Field | Type | Required |
|---|---|---|
| status | "draft" | "sent" | "awaiting_payment" | "partially_paid" | "paid" | No |
| due_date | string | No |
| notes | string | No |
Update invoice status and metadata. Money columns (amount, vat_total, total, number) are not writable. 'overdue' is derived, not stored, and is rejected. At least one field must be provided.
Example
curl -X PATCH https://app.crewflow.uk/api/v1/invoices/{id} \
-H "Authorization: Bearer crewflow_sk_..." \
-H "Content-Type: application/json" \
-d '{}'Quotes
/quotesread:quotesList quotes.
Example
curl https://app.crewflow.uk/api/v1/quotes \
-H "Authorization: Bearer crewflow_sk_..."/quoteswrite:quotesCreate a draft quote.
Request body
| Field | Type | Required |
|---|---|---|
| customer_id | string | Yes |
| property_id | string | No |
| lead_id | string | No |
| valid_until | string | No |
| notes | string | No |
| terms | string | No |
| line_items | QuoteLineItemInput[] | Yes |
Totals (subtotal, vat_total, total) are computed server-side from the line items — never send them.
line_items[] items (QuoteLineItemInput)
| Field | Type | Required |
|---|---|---|
| description | string | Yes |
| qty | number | Yes |
| unit | string | No |
| unit_price | number | Yes |
| vat_rate | 0 | 5 | 20 | Yes |
Example
curl -X POST https://app.crewflow.uk/api/v1/quotes \
-H "Authorization: Bearer crewflow_sk_..." \
-H "Content-Type: application/json" \
-d '{"customer_id":"...","line_items":[]}'Time
/timeread:timeList time entries.
Example
curl https://app.crewflow.uk/api/v1/time \
-H "Authorization: Bearer crewflow_sk_..."Staff
/staffread:staffList staff (role and join date only — no identity).
Example
curl https://app.crewflow.uk/api/v1/staff \
-H "Authorization: Bearer crewflow_sk_..."Expenses
/expensesread:expensesList expenses.
Example
curl https://app.crewflow.uk/api/v1/expenses \
-H "Authorization: Bearer crewflow_sk_..."/expenseswrite:expensesRecord an expense.
Request body
| Field | Type | Required |
|---|---|---|
| amount | number | Yes |
| vat_rate | 0 | 5 | 20 | No |
| category | string | No |
| notes | string | No |
| job_id | string | No |
Record a cost. vat_total is computed server-side (amount * vat_rate) and can never be sent. Currency is fixed to GBP.
Example
curl -X POST https://app.crewflow.uk/api/v1/expenses \
-H "Authorization: Bearer crewflow_sk_..." \
-H "Content-Type: application/json" \
-d '{"amount":0}'/expenses/{id}read:expensesFetch a single expense by id.
Example
curl https://app.crewflow.uk/api/v1/expenses/{id} \
-H "Authorization: Bearer crewflow_sk_..."/expenses/{id}write:expensesUpdate an expense.
Request body
| Field | Type | Required |
|---|---|---|
| amount | number | No |
| vat_rate | 0 | 5 | 20 | No |
| category | string | No |
| notes | string | No |
At least one field must be provided. Omitted fields are left unchanged. job_id and vat_total are not updatable.
Example
curl -X PATCH https://app.crewflow.uk/api/v1/expenses/{id} \
-H "Authorization: Bearer crewflow_sk_..." \
-H "Content-Type: application/json" \
-d '{}'Materials
/materialsread:materialsList material requests.
Example
curl https://app.crewflow.uk/api/v1/materials \
-H "Authorization: Bearer crewflow_sk_..."Versioning
A new major version gets a new path prefix; older versions continue to answer. Additive changes (new optional fields, new endpoints) are made in place.