API Documentation
Build custom workflows for your team with our open API. Looking for something specific? Reach out to us for support.
Getting started
Three steps to your first request. Looking for something specific? Reach out to us for support - we're happy to help.
- 1
Create an API key
Go to Settings → API Keys, name your key, and create it. It's shown once - store it somewhere safe. Keys are scoped to your organization.
- 2
Send it as a Bearer token
Every request includes the same header:
HeaderAuthorization: Bearer qbs_your_key_id_your_secret - 3
Make your first request
List your projects to grab a project id:
curlcurl "https://app.qubesheets.com/api/external/projects" \ -H "Authorization: Bearer qbs_your_key_id_your_secret"
Base URL
https://app.qubesheets.comFormat
JSON everywhere. Timestamps are ISO 8601 (UTC), timezones are IANA names, volume is cubic feet, weight is pounds, phone numbers are 10-digit US (normalized to +1).Projects
2/api/external/projectsList projects
Your organization's projects, most recently updated first.
- •Poll for changes cheaply by passing your last check time as updatedSince.
| Parameter | In | Required | Description |
|---|---|---|---|
| page | query | optional | Page number. Default 1. |
| limit | query | optional | Page size. Default 25, max 100. |
| updatedSince | query | optional | ISO 8601 timestamp - only projects updated at or after this time. |
curl "https://app.qubesheets.com/api/external/projects?limit=25" \
-H "Authorization: Bearer qbs_your_key_id_your_secret"{
"projects": [
{
"id": "665f1f77bcf86cd799439011",
"name": "Sarah Johnson",
"customerName": "Sarah Johnson",
"customerCompanyName": null,
"customerEmail": "sarah@example.com",
"phone": "+15551234567",
"origin": { "address": "123 Main St, Austin, TX", "unit": "4B" },
"destination": { "address": "456 Oak Ave, Dallas, TX", "unit": null },
"createdViaApi": true,
"createdAt": "2026-10-01T15:21:08.000Z",
"updatedAt": "2026-10-11T19:02:44.000Z"
}
],
"pagination": { "page": 1, "limit": 25, "total": 1, "totalPages": 1, "hasMore": false }
}/api/external/projectsCreate a project
Creates a project in your organization. When a phone number is included, the customer is automatically texted a video upload link.
| Parameter | In | Required | Description |
|---|---|---|---|
| customerName | body | required | Customer name. Also used as the project name. |
| phone | body | optional | 10-digit US phone number. Triggers the upload-link SMS. |
curl -X POST "https://app.qubesheets.com/api/external/projects" \
-H "Authorization: Bearer qbs_your_key_id_your_secret" \
-H "Content-Type: application/json" \
-d '{"customerName": "John Smith", "phone": "5551234567"}'{
"success": true,
"message": "Project created successfully",
"project": {
"id": "665f1f77bcf86cd799439011",
"name": "John Smith",
"customerName": "John Smith",
"phone": "+15551234567",
"createdAt": "2026-10-12T10:30:00.000Z",
"organizationId": "org_abc123",
"uploadLink": {
"attempted": true,
"sent": true,
"smsDelivered": true,
"uploadUrl": "https://app.qubesheets.com/customer-upload/abc123...",
"expiresAt": "2026-10-19T10:30:00.000Z"
}
}
}Inventory & AI
4/api/external/projects/{projectId}/inventoryGet inventory
Every inventory item on a project plus going-aware topline totals - the exact numbers your team sees on the project page in Qube Sheets.
- •cuftPerUnit / weightPerUnitLbs are per single unit; totalCuft / totalWeightLbs are per-unit × quantity.
- •Summary volume and weight count going units only and include recommended boxes; the WithoutRecommendedBoxes variants exclude them.
- •going is one of "going", "not going", "partial" - goingQuantity is the number of units actually moving.
| Parameter | In | Required | Description |
|---|---|---|---|
| projectId | path | required | Project id from List projects or Create a project. |
curl "https://app.qubesheets.com/api/external/projects/{projectId}/inventory" \
-H "Authorization: Bearer qbs_your_key_id_your_secret"{
"projectId": "665f1f77bcf86cd799439011",
"projectName": "Sarah Johnson",
"summary": {
"rowCount": 42,
"totalItems": 67,
"totalBoxes": 25,
"totalCuft": 812,
"totalWeightLbs": 5684,
"totalCuftWithoutRecommendedBoxes": 744,
"totalWeightLbsWithoutRecommendedBoxes": 5684,
"totalRooms": 6
},
"items": [
{
"id": "665f2a88bcf86cd799439aa1",
"name": "Sofa",
"description": "3-seat leather sofa",
"room": "Living Room",
"category": "Furniture",
"quantity": 1,
"going": "going",
"goingQuantity": 1,
"cuftPerUnit": 50,
"totalCuft": 50,
"weightPerUnitLbs": 350,
"totalWeightLbs": 350,
"itemType": "furniture",
"isBox": false,
"fragile": false,
"packedBy": "N/A",
"specialHandling": null,
"boxRecommendation": null,
"tags": [],
"aiGenerated": true,
"sourceType": "self_serve",
"createdAt": "2026-10-11T18:40:12.000Z",
"updatedAt": "2026-10-11T19:02:44.000Z"
}
]
}/api/external/projects/{projectId}/ai-summaryGet AI summary
The AI walkthrough summary for each completed video recording on the project.
| Parameter | In | Required | Description |
|---|---|---|---|
| projectId | path | required | Project id. |
curl "https://app.qubesheets.com/api/external/projects/{projectId}/ai-summary" \
-H "Authorization: Bearer qbs_your_key_id_your_secret"{
"projectId": "665f1f77bcf86cd799439011",
"recordings": [
{
"recordingId": "665f2b11bcf86cd799439bb2",
"recordedAt": "2026-10-11T18:12:00.000Z",
"source": "self_serve",
"summary": "Living room walkthrough: leather sofa and packed boxes identified..."
}
]
}/api/external/projects/{projectId}/packing-notesGet packing notes
AI-generated packing notes for each completed video recording on the project.
| Parameter | In | Required | Description |
|---|---|---|---|
| projectId | path | required | Project id. |
curl "https://app.qubesheets.com/api/external/projects/{projectId}/packing-notes" \
-H "Authorization: Bearer qbs_your_key_id_your_secret"{
"projectId": "665f1f77bcf86cd799439011",
"recordings": [
{
"recordingId": "665f2b11bcf86cd799439bb2",
"recordedAt": "2026-10-11T18:12:00.000Z",
"source": "self_serve",
"packingNotes": "Sofa needs shrink wrap; books already packed by owner..."
}
]
}/api/external/projects/{projectId}/customer-statementsGet customer statements
What the customer said during each recorded walkthrough - exact quotes with timestamps, linked to inventory items where the AI made the connection.
| Parameter | In | Required | Description |
|---|---|---|---|
| projectId | path | required | Project id. |
curl "https://app.qubesheets.com/api/external/projects/{projectId}/customer-statements" \
-H "Authorization: Bearer qbs_your_key_id_your_secret"{
"projectId": "665f1f77bcf86cd799439011",
"recordings": [
{
"recordingId": "665f2b11bcf86cd799439bb2",
"recordedAt": "2026-10-11T18:12:00.000Z",
"source": "self_serve",
"statements": [
{
"text": "The sofa is definitely coming with us.",
"timestamp": "00:15",
"relatedItem": "Sofa"
},
{
"text": "The treadmill stays, we sold it to the neighbors.",
"timestamp": "01:45",
"relatedItem": "Treadmill"
}
]
}
]
}Review links
2/api/external/projects/{projectId}/customer-review-linkGet customer review link
The shareable link where the customer reviews and signs off on their inventory, plus signature status.
- •Created automatically on first call; repeat calls return the same link.
- •Nothing is sent to the customer - you decide how the URL reaches them.
| Parameter | In | Required | Description |
|---|---|---|---|
| projectId | path | required | Project id. |
curl "https://app.qubesheets.com/api/external/projects/{projectId}/customer-review-link" \
-H "Authorization: Bearer qbs_your_key_id_your_secret"{
"projectId": "665f1f77bcf86cd799439011",
"reviewUrl": "https://app.qubesheets.com/inventory-review/3b5abf35...",
"customerName": "Sarah Johnson",
"customerPhone": "+15551234567",
"signed": true,
"signedAt": "2026-10-11T20:15:33.000Z",
"smsSentAt": null,
"expiresAt": null,
"createdAt": "2026-10-11T19:05:01.000Z"
}/api/external/projects/{projectId}/crew-review-linkGet crew review link
The shareable link where the crew views the inventory on moving day.
- •Created automatically on first call; repeat calls return the same link.
| Parameter | In | Required | Description |
|---|---|---|---|
| projectId | path | required | Project id. |
curl "https://app.qubesheets.com/api/external/projects/{projectId}/crew-review-link" \
-H "Authorization: Bearer qbs_your_key_id_your_secret"{
"projectId": "665f1f77bcf86cd799439011",
"crewReviewUrl": "https://app.qubesheets.com/crew-review/18de497e...",
"expiresAt": null,
"createdAt": "2026-10-11T19:05:02.000Z"
}Virtual calls
5/api/external/video-callsSchedule a virtual call
Schedules a new call and automatically sends a confirmation SMS to the customer with their unique join link. Returns both an agent join link (for your team) and a customer join link (already texted to the customer). Attach the call to an existing project with projectId, or create a new project on the fly with customerName.
- •Assign the call to a rep with assignedToEmail (their Qube Sheets login email) or assignedToUserId (from List users). Assigned calls appear on that rep's schedule, the SMS uses their name, and if they've connected Google Calendar the call lands on their calendar. New projects are assigned to the rep too.
- •Assignment is forgiving: if the email or user id doesn't match anyone in your organization, the call is still created unassigned and the response's assignment object says why - a bad email never blocks a booking.
- •The customerJoinLink is sent to the customer automatically. Use the agentJoinLink in your own calendar invites or CRM.
- •Join links are signed tokens that work without a login - valid from creation through 24 hours after the scheduled time.
- •Pass an IANA timezone (e.g. America/Chicago); the confirmation SMS shows the date/time in that timezone. Phone numbers are 10-digit US and normalized to +1 automatically.
- •Confirmation SMS uses your company name from Settings → Branding and your custom SMS template if configured.
| Parameter | In | Required | Description |
|---|---|---|---|
| projectId | body | optional | Attach the call to this existing project. Omit to create a new project instead. |
| customerName | body | optional | Required when creating a new project (no projectId). |
| customerPhone | body | required | 10-digit US phone number. Receives the confirmation SMS. |
| customerEmail | body | optional | Customer email, stored on the call. |
| scheduledFor | body | required | ISO 8601 timestamp, must be in the future. |
| timezone | body | optional | IANA timezone for the SMS display. Default America/New_York. |
| assignedToEmail | body | optional | Rep's Qube Sheets login email (case-insensitive). |
| assignedToUserId | body | optional | Rep's user id from List users. Wins if both fields are sent. |
curl -X POST "https://app.qubesheets.com/api/external/video-calls" \
-H "Authorization: Bearer qbs_your_key_id_your_secret" \
-H "Content-Type: application/json" \
-d '{
"customerName": "Jane Doe",
"customerPhone": "5551234567",
"customerEmail": "jane@example.com",
"scheduledFor": "2026-06-15T20:00:00.000Z",
"timezone": "America/New_York",
"assignedToEmail": "rep@yourcompany.com"
}'curl -X POST "https://app.qubesheets.com/api/external/video-calls" \
-H "Authorization: Bearer qbs_your_key_id_your_secret" \
-H "Content-Type: application/json" \
-d '{
"projectId": "507f1f77bcf86cd799439011",
"customerPhone": "5551234567",
"scheduledFor": "2026-06-15T20:00:00.000Z",
"assignedToEmail": "rep@yourcompany.com"
}'{
"success": true,
"message": "Video call scheduled successfully",
"videoCall": {
"id": "65f1b2c3d4e5f6a7b8c9d0e1",
"projectId": "65f1b2c3d4e5f6a7b8c9d0e2",
"roomId": "65f1b2c3d4e5f6a7b8c9d0e2-1717000000000-a1b2c3d4",
"scheduledFor": "2026-06-15T20:00:00.000Z",
"timezone": "America/New_York",
"status": "scheduled",
"customerName": "Jane Doe",
"customerPhone": "+15551234567",
"customerEmail": "jane@example.com",
"assignedTo": {
"userId": "user_2abc123def456",
"name": "Jane Smith",
"email": "rep@yourcompany.com"
},
"agentJoinLink": "https://app.qubesheets.com/join/video-call/65f1...?t=...",
"customerJoinLink": "https://app.qubesheets.com/join/video-call/65f1...?t=...",
"createdAt": "2026-05-01T12:00:00.000Z"
},
"assignment": {
"requested": "rep@yourcompany.com",
"assigned": true,
"calendarEventCreated": true
},
"confirmationSms": {
"attempted": true,
"delivered": true
}
}/api/external/video-callsList virtual calls
Your organization's scheduled video calls, with filters for status, project, date range, and assignee.
- •Filter to one rep's calls with assignedTo - pass a user id or email, e.g. ?assignedTo=rep@yourcompany.com&upcoming=true.
| Parameter | In | Required | Description |
|---|---|---|---|
| upcoming | query | optional | true - only future calls. |
| status | query | optional | Filter by call status, e.g. scheduled, cancelled. |
| projectId | query | optional | Only calls on this project. |
| startDate | query | optional | ISO 8601 - calls scheduled at or after this time. |
| endDate | query | optional | ISO 8601 - calls scheduled at or before this time. |
| assignedTo | query | optional | User id or email - only that rep's calls. |
| limit | query | optional | Page size. |
| skip | query | optional | Number of results to skip (offset pagination). |
curl "https://app.qubesheets.com/api/external/video-calls?upcoming=true&limit=20" \
-H "Authorization: Bearer qbs_your_key_id_your_secret"{
"success": true,
"videoCalls": [
{
"id": "65f1b2c3d4e5f6a7b8c9d0e1",
"projectId": "65f1b2c3d4e5f6a7b8c9d0e2",
"roomId": "65f1b2c3d4e5f6a7b8c9d0e2-1717000000000-a1b2c3d4",
"scheduledFor": "2026-06-15T20:00:00.000Z",
"timezone": "America/New_York",
"status": "scheduled",
"customerName": "Jane Doe",
"customerPhone": "+15551234567",
"customerEmail": "jane@example.com",
"assignedTo": {
"userId": "user_2abc123def456",
"name": "Jane Smith",
"email": "rep@yourcompany.com"
},
"agentJoinLink": "https://app.qubesheets.com/join/video-call/...",
"customerJoinLink": "https://app.qubesheets.com/join/video-call/...",
"createdAt": "2026-05-01T12:00:00.000Z",
"updatedAt": "2026-05-01T12:00:00.000Z"
}
],
"pagination": { "total": 1, "limit": 20, "skip": 0, "returned": 1 }
}/api/external/video-calls/{callId}Get a virtual call
A single video call, including the SMS reminders already sent to the customer.
| Parameter | In | Required | Description |
|---|---|---|---|
| callId | path | required | Video call id. |
curl "https://app.qubesheets.com/api/external/video-calls/{callId}" \
-H "Authorization: Bearer qbs_your_key_id_your_secret"{
"success": true,
"videoCall": {
"id": "65f1b2c3d4e5f6a7b8c9d0e1",
"projectId": "65f1b2c3d4e5f6a7b8c9d0e2",
"roomId": "65f1b2c3d4e5f6a7b8c9d0e2-1717000000000-a1b2c3d4",
"scheduledFor": "2026-06-15T20:00:00.000Z",
"timezone": "America/New_York",
"status": "scheduled",
"customerName": "Jane Doe",
"customerPhone": "+15551234567",
"customerEmail": "jane@example.com",
"assignedTo": {
"userId": "user_2abc123def456",
"name": "Jane Smith",
"email": "rep@yourcompany.com"
},
"agentJoinLink": "https://app.qubesheets.com/join/video-call/...",
"customerJoinLink": "https://app.qubesheets.com/join/video-call/...",
"remindersSent": [
{ "type": "confirmation", "sentAt": "2026-05-01T12:00:01.000Z", "method": "sms" }
],
"createdAt": "2026-05-01T12:00:00.000Z",
"updatedAt": "2026-05-01T12:00:00.000Z"
}
}/api/external/video-calls/{callId}Reschedule or reassign a call
Updates a scheduled call. Provide scheduledFor to reschedule, assignedToEmail / assignedToUserId to reassign it to a different team member, or both at once. Only calls with status scheduled can be updated.
- •Rescheduling automatically sends a reschedule SMS to the customer with their (still valid) join link, and moves the rep's Google Calendar event if one exists.
- •Reassigning moves the call to the new rep's schedule (and their Google Calendar, if connected). No SMS is sent for a reassignment alone.
| Parameter | In | Required | Description |
|---|---|---|---|
| callId | path | required | Video call id. |
| scheduledFor | body | optional | New ISO 8601 time, must be in the future. |
| timezone | body | optional | IANA timezone for the reschedule SMS display. |
| assignedToEmail | body | optional | New rep's Qube Sheets login email. |
| assignedToUserId | body | optional | New rep's user id. Wins if both fields are sent. |
curl -X PATCH "https://app.qubesheets.com/api/external/video-calls/{callId}" \
-H "Authorization: Bearer qbs_your_key_id_your_secret" \
-H "Content-Type: application/json" \
-d '{
"scheduledFor": "2026-06-16T21:00:00.000Z",
"timezone": "America/New_York"
}'curl -X PATCH "https://app.qubesheets.com/api/external/video-calls/{callId}" \
-H "Authorization: Bearer qbs_your_key_id_your_secret" \
-H "Content-Type: application/json" \
-d '{ "assignedToEmail": "otherrep@yourcompany.com" }'{
"success": true,
"message": "Video call rescheduled successfully",
"videoCall": {
"id": "65f1b2c3d4e5f6a7b8c9d0e1",
"projectId": "65f1b2c3d4e5f6a7b8c9d0e2",
"scheduledFor": "2026-06-16T21:00:00.000Z",
"timezone": "America/New_York",
"status": "scheduled",
"assignedTo": {
"userId": "user_2abc123def456",
"name": "Jane Smith",
"email": "rep@yourcompany.com"
},
"agentJoinLink": "https://app.qubesheets.com/join/video-call/...",
"customerJoinLink": "https://app.qubesheets.com/join/video-call/..."
},
"rescheduleSms": {
"attempted": true,
"delivered": true
}
}/api/external/video-calls/{callId}Cancel a call
Cancels a scheduled call. Optionally sends a cancellation SMS to the customer. Only calls with status scheduled can be cancelled.
| Parameter | In | Required | Description |
|---|---|---|---|
| callId | path | required | Video call id. |
| sendSms | body | optional | true - text the customer a cancellation notice. Default false. |
curl -X DELETE "https://app.qubesheets.com/api/external/video-calls/{callId}" \
-H "Authorization: Bearer qbs_your_key_id_your_secret" \
-H "Content-Type: application/json" \
-d '{ "sendSms": true }'{
"success": true,
"message": "Video call cancelled",
"videoCall": {
"id": "65f1b2c3d4e5f6a7b8c9d0e1",
"status": "cancelled"
},
"cancellationSms": {
"attempted": true,
"delivered": true
}
}Capture links
2/api/external/projects/{projectId}/self-survey-linkGet customer self-survey link
The link where the customer records their own video walkthrough - the same URL the app texts when you create a project with a phone number.
- •Returns the active unexpired link if one exists; otherwise creates a fresh one with a 7-day expiry. An expired link is replaced on the next call.
- •Nothing is texted - you decide how the URL reaches the customer.
| Parameter | In | Required | Description |
|---|---|---|---|
| projectId | path | required | Project id. |
curl "https://app.qubesheets.com/api/external/projects/{projectId}/self-survey-link" \
-H "Authorization: Bearer qbs_your_key_id_your_secret"{
"projectId": "665f1f77bcf86cd799439011",
"selfSurveyUrl": "https://app.qubesheets.com/customer-upload/9f2c41d8...",
"customerName": "Sarah Johnson",
"customerPhone": "+15551234567",
"expiresAt": "2026-10-19T10:30:00.000Z",
"createdAt": "2026-10-12T10:30:00.000Z"
}/api/external/projects/{projectId}/onsite-walkthrough-linkGet on-site walkthrough link
The link your surveyor opens to record a walkthrough on site. Same capture UI as the self-survey, but customer SMS notifications are suppressed and completion returns to the project, since the person recording is your team member.
- •Created automatically on first call; repeat calls return the same link. Walkthrough links never expire.
| Parameter | In | Required | Description |
|---|---|---|---|
| projectId | path | required | Project id. |
curl "https://app.qubesheets.com/api/external/projects/{projectId}/onsite-walkthrough-link" \
-H "Authorization: Bearer qbs_your_key_id_your_secret"{
"projectId": "665f1f77bcf86cd799439011",
"walkthroughUrl": "https://app.qubesheets.com/customer-upload/4a7be1c2...",
"createdAt": "2026-10-12T10:30:00.000Z"
}Team
1/api/external/usersList users
The members of your organization. Use the returned id (as assignedToUserId) or email (as assignedToEmail) to assign video calls to a specific team member.
- •Email matching on call assignment is case-insensitive against the member's Qube Sheets login email - if your CRM stores a different address for the same person, fetch the correct emails here or assign by assignedToUserId instead.
curl "https://app.qubesheets.com/api/external/users" \
-H "Authorization: Bearer qbs_your_key_id_your_secret"{
"success": true,
"users": [
{
"id": "user_2abc123def456",
"email": "rep@yourcompany.com",
"firstName": "Jane",
"lastName": "Smith",
"name": "Jane Smith",
"role": "org:member"
}
],
"total": 1
}Errors
Errors always return { "error", "message" } with one of these status codes:
| Status | Meaning |
|---|---|
400 | Invalid request - a parameter failed validation (the message says which), e.g. a scheduledFor in the past. |
401 | Missing or invalid API key. |
404 | Project or video call not found, or it belongs to a different organization. |
500 | Something went wrong on our side - safe to retry. |
A machine-readable index of every endpoint is also available at GET https://app.qubesheets.com/api/external (no auth required).