API Overview
GraphQL Reference

GraphQL API Reference

Queries and mutations for jobs, calendar events, charges, invoices, transactions, and users. Since introspection is enabled on the playground, this page focuses on practical usage patterns — the playground is the authoritative source for the full schema.

Open GraphQL Playground →

Querying Jobs

The jobs query is the primary way to fetch job data. It supports filtering, pagination, sorting, and lazy-loading of related data via the resolve parameter.

query ($filter: JobsFilter, $skip: Int, $limit: Int, $sort: String, $resolve: [String]) {
  jobs(
    filter: $filter
    skip: $skip
    limit: $limit
    sort: $sort
    resolve: $resolve
  ) {
    total
    skipped
    limited
    jobs {
      id
      code
      stage
      state
      currency
      total
      createdAt
    }
  }
}
Remember: Pass your zone ID as a query parameter: ?zone=your-zone-id. Without a zone, the query uses the zone resolved from your auth context.

Job Fields

Key fields on the Job type. Use the playground to explore the complete schema.

Field Type Description
id String UUID — use for all CRUD operations and references.
code String Human-readable identifier in the format XXXX-XXX-XXX. The first 4 characters indicate the zone. Use id for programmatic operations.
stage String Workflow stage: "lead" | "estimate" | "booking" | "invoice" | "cancelled"
state String "open" | "closed" | "archived" | "deleted"
total Int Sum of all charges in cents, including taxes and discounts.
taxTotal Int Total taxes in cents.
paidTotal Int Amount paid in cents.
pendingTotal Int Outstanding balance in cents.
damageTotal Int Damage charges in cents.
discountTotal Int Total discounts in cents.
discountedSubTotal Int Sub-total after discounts, in cents.
currency String ISO 4217 currency code, e.g. "USD", "CAD".
createdAt Int Unix timestamp (seconds) when the job was created.
updatedAt Int Unix timestamp (seconds) of last update.
archivedAt Int Unix timestamp when archived, or null.
closedAt Int Unix timestamp when closed, or null.
timeline String Preferred move date in YYYY-MM-DD format.
users Array Customer, crew, and agent assignments. Requires resolve: ["users"].
locations Array Origin, destination, and dock locations. Requires resolve: ["locations"].
discounts Array Applied discounts. Requires resolve: ["discounts"].

Filtering & Pagination

skip

Number of records to skip. Default: 0. Use with limit for pagination.

limit

Max records to return. Default: 50. Use total in the response to calculate pages.

sort

Format: "field:asc" or "field:desc". Default: "createdAt:desc". Supports: createdAt, updatedAt, id, stage, closedAt, currency.

Filter fields

Filter field Description
jobIds Array of job UUIDs. Returns only jobs with these IDs.
code Job short code, e.g. "TLED-GVQ-XZN".
stage Filter by stage: "lead", "estimate", "booking", "invoice", "cancelled".
state Filter by state: "open", "closed", "archived", "deleted".
search Full-text search across customer name, email, phone, job stage, and job code.
userSearch Find all jobs for a specific user. Pass { userId, role } — role options: "customer", "Crew Member", "Crew Lead".
zoneDir Zone direction: "lte" (self + descendants), "gte" (self + ancestors), "eq" (exact zone only). Default: "lte".
createdAfter Unix timestamp (seconds). Returns jobs created after this time.
createdBefore Unix timestamp (seconds). Returns jobs created before this time.

User search example

// Variables:
{
  "filter": {
    "userSearch": {
      "userId": "354dfbd0-8d1b-11ed-9001-89ed3e4c1f5d",
      "role": "customer"
    }
  },
  "limit": 20,
  "sort": "createdAt:desc"
}

Resolve — Lazy-loaded Fields

For performance, the users, locations, and discounts fields on jobs are not populated by default. Querying them without resolve always returns null. Pass the field names you want populated in the resolve array:

// Variables:
{
  "resolve": ["users", "locations", "discounts"]
}

// Query:
query ($resolve: [String]) {
  jobs(resolve: $resolve) {
    jobs {
      users {
        user {
          givenName
          familyName
          email
          phone
        }
        role
      }
      locations {
        location {
          addressLineOne
          city
          countryJurisdiction
          areaCode
          coordinates { latitude longitude }
        }
        locationType
      }
    }
  }
}
Only pass the resolve fields you actually need. Each resolved field triggers additional database queries. For large result sets, omit resolve entirely and fetch related data separately by ID.

Creating Jobs

The createJob mutation creates a new job. The only required field is stage.

Minimal example

mutation {
  createJob(stage: "lead") {
    id
    code
  }
}

With an existing customer

mutation {
  createJob(
    stage: "lead"
    users: [
      {
        userId: "354dfbd0-8d1b-11ed-9001-89ed3e4c1f5d"
        role: "customer"
      }
    ]
  ) {
    id
    code
  }
}

For creating jobs with full lead data (name, phone, address, move date), use the Intake API — it handles customer deduplication, geocoding, and zone routing automatically.

Updating Jobs

The updateJobs mutation accepts an array of update inputs — you can update multiple jobs in one request. The only required field is jobId.

mutation ($updateJobs: [UpdateJobInput]) {
  updateJobs(updateJobs: $updateJobs) {
    isSuccess
  }
}

// Variables:
{
  "updateJobs": [
    {
      "jobId": "7af1bc10-e356-11ef-8bf8-7dd6e69e92c7",
      "stage": "booking",
      "timeline": "2026-09-15"
    }
  ]
}

Adding and removing users

{
  "updateJobs": [
    {
      "jobId": "7af1bc10-...",
      "addUsers": [
        { "userId": "user-uuid", "role": "customer" }
      ],
      "removeUsers": ["other-user-uuid"]
    }
  ]
}

Adding and removing locations

{
  "updateJobs": [
    {
      "jobId": "7af1bc10-...",
      "addLocations": [
        {
          "locationId": "location-uuid",
          "locationType": "start"   // "start" | "end" | "dock"
        }
      ],
      "removeLocations": ["other-location-uuid"]
    }
  ]
}

Charges

All charges are associated with a calendar event. Create the event first, then add charges to the job referencing that event's ID.

Add charge from an existing product

{
  "updateJobs": [
    {
      "jobId": "7af1bc10-...",
      "newCharges": {
        "charges": [
          {
            "eventId": "event-uuid",
            "productId": "product-uuid",
            "quantity": 1
          }
        ],
        "userId": "customer-user-uuid"
      }
    }
  ]
}

Add a custom charge (no product ID needed)

{
  "updateJobs": [
    {
      "jobId": "7af1bc10-...",
      "newCharges": {
        "charges": [
          {
            "eventId": "event-uuid",
            "productName": "Long distance surcharge",
            "currency": "USD",
            "amount": 15000,   // in cents = $150.00
            "quantity": 1
          }
        ]
      }
    }
  ]
}

Remove charges

{
  "updateJobs": [
    {
      "jobId": "7af1bc10-...",
      "removeCharges": ["charge-uuid-1", "charge-uuid-2"]
    }
  ]
}

Calendar Events

Calendar events represent appointments, moves, estimates, and invoiceable service windows. Many operations (charges, invoices) require an event ID. Events have a type and a status lifecycle.

Create an event

mutation ($calendarEvents: [createCalendarEventInput]!) {
  createCalendarEvent(calendarEvents: $calendarEvents) {
    events { id }
  }
}

// Variables:
{
  "calendarEvents": [
    {
      "title": "Move day",
      "type": "move",
      "jobId": "7af1bc10-...",
      "status": "required",
      "start": 1756396800,    // Unix timestamp (seconds)
      "end": 1756411200
    }
  ]
}
Event type Use case
move A scheduled moving appointment
estimating An on-site or virtual estimate appointment
virtualEstimate A virtual (video/phone) estimate — triggers virtual estimate flow
storage A storage-related service event

Update an event (mark as completed, set times)

mutation ($edit: editCalendarEventInput!, $ids: [String!]) {
  editCalendarEvent(edit: $edit, ids: $ids)
}

// Variables:
{
  "ids": ["event-uuid"],
  "edit": {
    "status": "completed",
    "start": 1756396800,
    "end": 1756411200
  }
}

Remove an event

mutation ($ids: [String!]!) {
  removeCalendarEvent(ids: $ids)
}

// Variables:
{ "ids": ["event-uuid"] }

Invoices

Invoices are created from one or more calendar events. Only events that are both scheduled (have start and end times) and in completed status can be invoiced.

mutation ($invoices: [CreateInvoiceInput!]!) {
  createInvoice(invoices: $invoices) {
    invoices { id }
  }
}

// Variables:
{
  "invoices": [
    {
      "eventIds": ["event-uuid-1", "event-uuid-2"]
    }
  ]
}

The returned invoice id is needed when creating transactions (payments).

Transactions

Transactions represent payments, refunds, and deposits. Before creating a transaction, query the available payment types on the zone.

Step 1 — Query payment types

query {
  getConfigValues(keys: ["payment-type.*"]) {
    id
    value   // JSON string containing name, enabled, etc.
  }
}

Step 2 — Create a transaction

mutation ($amount: Int!, $customerId: String!, $jobId: String!,
         $invoiceId: String, $type: TransactionType!,
         $paymentTypeId: String!, $stage: TransactionStage,
         $metadata: Metadata, $disableSendReceipt: Boolean) {
  createTransaction(
    amount: $amount
    customerId: $customerId
    jobId: $jobId
    invoiceId: $invoiceId
    type: $type
    paymentTypeId: $paymentTypeId
    stage: $stage
    metadata: $metadata
    disableSendReceipt: $disableSendReceipt
  ) {
    transaction { id }
  }
}

// Variables:
{
  "amount": 35000,                          // in cents = $350.00
  "jobId": "7af1bc10-...",
  "customerId": "customer-user-uuid",
  "invoiceId": "invoice-uuid",              // required for "payment" and "refund"
  "type": "payment",                        // "payment" | "refund" | "deposit"
  "stage": "paid",
  "paymentTypeId": "payment-type-config-id",
  "disableSendReceipt": false,
  "metadata": { "origin": "my-integration" }
}
Transaction types:
payment — requires an invoiceId
refund — requires an invoiceId
deposit — does not require an invoiceId

Config Values

Zone configuration drives many aspects of MoveRight's behavior — business hours, payment types, branding, autodialer settings, and more. You can read config values via the getConfigValues query.

// Get all payment types for the zone
query {
  getConfigValues(keys: ["payment-type.*"]) {
    id
    value   // JSON string — parse to get name, enabled, etc.
  }
}

// Get a specific config value
query {
  getConfigValues(keys: ["branding.companyName"]) {
    id
    value
  }
}

The keys input supports wildcards via *. The value field is always a JSON string — parse it to get the actual value.

Explore the full schema

Introspection is enabled. The playground is the authoritative reference for every available type, field, query, and mutation.

Open GraphQL Playground →