qube
sheets
Open API

API Documentation

Build custom workflows for your team with our open API. Looking for something specific? Reach out to us for support.

https://app.qubesheets.comBearer token authJSON everywhere

Getting started

Three steps to your first request. Looking for something specific? Reach out to us for support - we're happy to help.

  1. 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. 2

    Send it as a Bearer token

    Every request includes the same header:

    Header
    Authorization: Bearer qbs_your_key_id_your_secret
  3. 3

    Make your first request

    List your projects to grab a project id:

    curl
    curl "https://app.qubesheets.com/api/external/projects" \
      -H "Authorization: Bearer qbs_your_key_id_your_secret"

Base URL

https://app.qubesheets.com

Format

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
GET/api/external/projects

List projects

Your organization's projects, most recently updated first.

  • •Poll for changes cheaply by passing your last check time as updatedSince.
ParameterInRequiredDescription
pagequeryoptionalPage number. Default 1.
limitqueryoptionalPage size. Default 25, max 100.
updatedSincequeryoptionalISO 8601 timestamp - only projects updated at or after this time.
curl
curl "https://app.qubesheets.com/api/external/projects?limit=25" \
  -H "Authorization: Bearer qbs_your_key_id_your_secret"
Example response
{
  "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 }
}
POST/api/external/projects

Create a project

Creates a project in your organization. When a phone number is included, the customer is automatically texted a video upload link.

ParameterInRequiredDescription
customerNamebodyrequiredCustomer name. Also used as the project name.
phonebodyoptional10-digit US phone number. Triggers the upload-link SMS.
curl
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"}'
Example response
{
  "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
GET/api/external/projects/{projectId}/inventory

Get 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.
ParameterInRequiredDescription
projectIdpathrequiredProject id from List projects or Create a project.
curl
curl "https://app.qubesheets.com/api/external/projects/{projectId}/inventory" \
  -H "Authorization: Bearer qbs_your_key_id_your_secret"
Example response
{
  "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"
    }
  ]
}
GET/api/external/projects/{projectId}/ai-summary

Get AI summary

The AI walkthrough summary for each completed video recording on the project.

ParameterInRequiredDescription
projectIdpathrequiredProject id.
curl
curl "https://app.qubesheets.com/api/external/projects/{projectId}/ai-summary" \
  -H "Authorization: Bearer qbs_your_key_id_your_secret"
Example response
{
  "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..."
    }
  ]
}
GET/api/external/projects/{projectId}/packing-notes

Get packing notes

AI-generated packing notes for each completed video recording on the project.

ParameterInRequiredDescription
projectIdpathrequiredProject id.
curl
curl "https://app.qubesheets.com/api/external/projects/{projectId}/packing-notes" \
  -H "Authorization: Bearer qbs_your_key_id_your_secret"
Example response
{
  "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..."
    }
  ]
}
GET/api/external/projects/{projectId}/customer-statements

Get customer statements

What the customer said during each recorded walkthrough - exact quotes with timestamps, linked to inventory items where the AI made the connection.

ParameterInRequiredDescription
projectIdpathrequiredProject id.
curl
curl "https://app.qubesheets.com/api/external/projects/{projectId}/customer-statements" \
  -H "Authorization: Bearer qbs_your_key_id_your_secret"
Example response
{
  "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

Virtual calls

5
POST/api/external/video-calls

Schedule 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.
ParameterInRequiredDescription
projectIdbodyoptionalAttach the call to this existing project. Omit to create a new project instead.
customerNamebodyoptionalRequired when creating a new project (no projectId).
customerPhonebodyrequired10-digit US phone number. Receives the confirmation SMS.
customerEmailbodyoptionalCustomer email, stored on the call.
scheduledForbodyrequiredISO 8601 timestamp, must be in the future.
timezonebodyoptionalIANA timezone for the SMS display. Default America/New_York.
assignedToEmailbodyoptionalRep's Qube Sheets login email (case-insensitive).
assignedToUserIdbodyoptionalRep's user id from List users. Wins if both fields are sent.
Create new project + schedule call
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"
  }'
Schedule call on existing project
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"
  }'
Example response
{
  "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
  }
}
GET/api/external/video-calls

List 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.
ParameterInRequiredDescription
upcomingqueryoptionaltrue - only future calls.
statusqueryoptionalFilter by call status, e.g. scheduled, cancelled.
projectIdqueryoptionalOnly calls on this project.
startDatequeryoptionalISO 8601 - calls scheduled at or after this time.
endDatequeryoptionalISO 8601 - calls scheduled at or before this time.
assignedToqueryoptionalUser id or email - only that rep's calls.
limitqueryoptionalPage size.
skipqueryoptionalNumber of results to skip (offset pagination).
curl
curl "https://app.qubesheets.com/api/external/video-calls?upcoming=true&limit=20" \
  -H "Authorization: Bearer qbs_your_key_id_your_secret"
Example response
{
  "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 }
}
GET/api/external/video-calls/{callId}

Get a virtual call

A single video call, including the SMS reminders already sent to the customer.

ParameterInRequiredDescription
callIdpathrequiredVideo call id.
curl
curl "https://app.qubesheets.com/api/external/video-calls/{callId}" \
  -H "Authorization: Bearer qbs_your_key_id_your_secret"
Example response
{
  "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"
  }
}
PATCH/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.
ParameterInRequiredDescription
callIdpathrequiredVideo call id.
scheduledForbodyoptionalNew ISO 8601 time, must be in the future.
timezonebodyoptionalIANA timezone for the reschedule SMS display.
assignedToEmailbodyoptionalNew rep's Qube Sheets login email.
assignedToUserIdbodyoptionalNew rep's user id. Wins if both fields are sent.
Reschedule
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"
  }'
Reassign only
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" }'
Example response
{
  "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
  }
}
DELETE/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.

ParameterInRequiredDescription
callIdpathrequiredVideo call id.
sendSmsbodyoptionaltrue - text the customer a cancellation notice. Default false.
curl
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 }'
Example response
{
  "success": true,
  "message": "Video call cancelled",
  "videoCall": {
    "id": "65f1b2c3d4e5f6a7b8c9d0e1",
    "status": "cancelled"
  },
  "cancellationSms": {
    "attempted": true,
    "delivered": true
  }
}

Capture links

2

Team

1
GET/api/external/users

List 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
curl "https://app.qubesheets.com/api/external/users" \
  -H "Authorization: Bearer qbs_your_key_id_your_secret"
Example response
{
  "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:

StatusMeaning
400Invalid request - a parameter failed validation (the message says which), e.g. a scheduledFor in the past.
401Missing or invalid API key.
404Project or video call not found, or it belongs to a different organization.
500Something 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).

Qube Sheets - AI inventory for moving companies. Questions? Reach out to us for support.