Liftify API (1.0)

Download OpenAPI specification:

Developer Documentation for Liftify's API

Welcome to the API documentation for Liftify, a comprehensive reputation management solution. Liftify is engineered to help businesses generate authentic reviews, enhance conversion rates, and ultimately drive revenue growth.

This platform provides an array of tools and features that empower users to effectively manage their online reputation, engage with customers, and gain valuable insights. With its user-friendly interface and robust functionality, Liftify simplifies the process of collecting and managing customer feedback, allowing businesses to focus on delivering exceptional service and achieving their growth objectives. Whether you're a small business owner or part of a large enterprise, Liftify offers scalable solutions tailored to meet your specific needs. Explore our API documentation to discover how Liftify can transform your approach to reputation management and contribute to your business success.

The Liftify's API can be accessed by a Bearer token that is used by our team. Add the token to the Authorization header of a request.

The API is reachable at https://app.liftify.com/api.

Customers

Manage customers and their campaign enrollments

Retrieve customers

Retrieve a paginated list of customers with optional filtering by account and location.

Expandable fields: customer_campaigns, reviews

When expanded, customer_campaigns includes the full campaign object with campaign name, type, status, location, and timestamps. When not expanded, the field is omitted from the response.

When expanded, reviews includes the full review objects for each confirmed customer-review match. When not expanded (but expand options are present in the request), reviews returns an array of review_uuid strings. When no expand options are present, the reviews field is omitted from the response.

Authorizations:
BearerAuth
query Parameters
string or Array of strings

The location ID in the format of "loc-" followed by the UUID.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

expand
Array of strings

Expand related objects inline instead of returning IDs. Pass one or more association names to include the full object in the response. Use dot-notation for nested expansion (e.g., expand[]=customer_campaigns.location).

Limits: Maximum 10 expand paths per request, maximum 3 levels of nesting depth.

Without expand: Related objects are either omitted or rendered as compact inline objects (e.g., { "location_id": "loc-...", "name": "..." }).

With expand: Related objects are rendered as full API objects with all their fields.

Error handling: Invalid expand paths return a 400 Bad Request with a message listing valid expansion options.

has_reviews
boolean

Filter customers that have or don't have confirmed review matches. When true, returns only customers with at least one confirmed review match. When false, returns only customers without any confirmed review matches.

string or Array of strings
Examples:
  • status=unsubscribed - Single status
  • status=active&status=scheduled - Multiple statuses (array form, e.g. status[]=active&status[]=scheduled)

Filter customers by lifecycle status. Accepts a single value or an array of values; values are case-insensitive. When multiple values are provided, customers matching any of them are returned. Invalid values return a 400 response.

last_activity_at_gt
string <date-time>
Examples:
  • last_activity_at_gt=2025-08-15T14:30:00-04:00 - Full ISO 8601 with timezone offset (strictly greater than this instant)
  • last_activity_at_gt=2025-08-15T18:30:00Z - UTC timestamp (Z)
  • last_activity_at_gt=2025-08-15T18:30:00.123Z - Millisecond precision

Return customers with a last_activity_at strictly greater than this value. This is an ISO 8601 date-time string. Ignored when last_activity_at_eq is present.

last_activity_at_gte
string <date-time>
Examples:
  • last_activity_at_gte=2025-08-15T00:00:00Z - Start of day inclusive (UTC)
  • last_activity_at_gte=2025-08-15T09:15:00Z - Specific time inclusive threshold
  • last_activity_at_gte=2025-10-31T00:00:00-05:00 - Edge case near boundary with timezone offset

Return customers with a last_activity_at greater than or equal to this value. This is an ISO 8601 date-time string. Ignored when last_activity_at_eq is present.

last_activity_at_lt
string <date-time>
Examples:
  • last_activity_at_lt=2025-08-15T23:59:59Z - End of day exclusive threshold (use 23:59:59)
  • last_activity_at_lt=2026-01-01T00:00:00Z - Exclude anything on/after this future time
  • last_activity_at_lt=2025-08-16T00:00:00+02:00 - Timezone offset representation

Return customers with a last_activity_at strictly less than this value. This is an ISO 8601 date-time string. Ignored when last_activity_at_eq is present.

last_activity_at_lte
string <date-time>
Examples:
  • last_activity_at_lte=2025-08-15T23:59:59Z - Inclusive end-of-period filtering
  • last_activity_at_lte=2025-08-15T23:59:59Z - Use with last_activity_at_gte to form a closed interval
  • last_activity_at_lte=2025-08-15T23:59:59.999Z - Millisecond precision inclusive

Return customers with a last_activity_at less than or equal to this value. This is an ISO 8601 date-time string. Ignored when last_activity_at_eq is present.

string or string
Examples:
  • last_activity_at_eq=2025-08-15 - Date (YYYY-MM-DD)
  • last_activity_at_eq=2020-02-29 - Leap year date (time ignored)
  • last_activity_at_eq=2025-01-31 - End of month date (time ignored)
  • last_activity_at_eq=2025-08-15T00:00:00Z - Date-time (time is ignored, date portion used)
  • last_activity_at_eq=2025-08-15T22:00:00-04:00 - Date-time with timezone offset (time ignored)
  • last_activity_at_eq=2025-08-15T10:30:00.789Z - Date-time with milliseconds (time ignored)

Filter customers whose last activity occurred on the specified date, ignoring the time portion. Accepts either an ISO‑8601 date (YYYY‑MM‑DD) or a full ISO‑8601 date-time; only the date portion is used for filtering.

Precedence: When last_activity_at_eq is provided, all other date-time filters (last_activity_at_gt, last_activity_at_gte, last_activity_at_lt, last_activity_at_lte) are ignored.

campaign_template_id
string
Examples:
  • campaign_template_id=abc123def456 - Campaign public identifier

Return only customers that have at least one touchpoint belonging to this campaign (identified by its campaign_template_id as returned by the Campaigns endpoint). Combine with touchpoint_sequence_order and/or touchpoint_status to narrow to a specific touchpoint. An unknown or inaccessible campaign returns a 404 response.

touchpoint_sequence_order
integer >= 1
Examples:
  • touchpoint_sequence_order=2 - Second touchpoint in the campaign sequence

Return only customers that have a touchpoint at this step of the campaign sequence (1-based). Requires campaign_template_id; using it alone returns a 400 response. Non-positive or non-integer values return a 400 response.

string or Array of strings
Examples:
  • touchpoint_status=sent - Single status
  • touchpoint_status=sent&touchpoint_status=delivered - Multiple statuses (array form, e.g. touchpoint_status[]=sent&touchpoint_status[]=delivered)

Return only customers whose matching touchpoint is in one of these delivery states. Accepts a single value or an array of values; values are case-insensitive. Requires campaign_template_id; using it alone returns a 400 response. When omitted, touchpoints in any state match. Invalid values return a 400 response. Applies to the same touchpoint records selected by campaign_template_id / touchpoint_sequence_order.

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of customers matching the filter criteria

total_pages
integer <int32>

Total number of pages available

current_page
integer <int8>

Current page number

Array of objects (CustomerListItem)

Response samples

Content type
application/json
{
  • "total_count": 150,
  • "total_pages": 2,
  • "current_page": 1,
  • "customers": [
    • {
      • "customer_id": "cus-550e8400-e29b-41d4-a716-446655440000",
      • "first_name": "Jane",
      • "last_name": "Smith",
      • "email": "[email protected]",
      • "phone_number": "+15551234568",
      • "lead_source": "google",
      • "normalized_lead_source": "google",
      • "lead_source_channel_type": "Google",
      • "status": "active",
      • "text_opt_in": true,
      • "created_at": "2025-08-15T16:37:20Z",
      • "updated_at": "2025-08-16T08:50:18Z",
      • "account_id": "acc-test-402efb14-f779-4dfe-9a07-feb72602c8bb",
      • "locations": [
        • {
          • "location_id": "loc-test-8c0a8daa-dbd8-4e7c-bd38-d810795512e3",
          • "name": "Test Location"
          }
        ],
      • "custom_fields": [
        • {
          • "name": "campaign",
          • "value": "summer_promotion",
          • "label": "Campaign",
          • "type": "text"
          }
        ]
      },
    • {
      • "customer_id": "cus-660e8400-e29b-41d4-a716-446655440001",
      • "first_name": "John",
      • "last_name": "Doe",
      • "email": "[email protected]",
      • "phone_number": "+15551234567",
      • "lead_source": "yelp",
      • "normalized_lead_source": "yelp",
      • "lead_source_channel_type": "Yelp",
      • "status": "unsubscribed",
      • "text_opt_in": false,
      • "created_at": "2025-08-10T14:22:15Z",
      • "updated_at": "2025-08-15T16:37:20Z",
      • "account_id": "acc-test-402efb14-f779-4dfe-9a07-feb72602c8bb",
      • "locations": [
        • {
          • "location_id": "loc-test-8c0a8daa-dbd8-4e7c-bd38-d810795512e3",
          • "name": "Test Location"
          }
        ],
      • "custom_fields": [ ]
      }
    ]
}

Create or update a Customer

Create or update a Customer. This endpoint will NOT associated or start a campaign for a contact. A staus of unsubscribed will stop a campaign.

Authorizations:
BearerAuth
Request Body schema: application/json
Any of
account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

location_id
required
string

Location ID starts with "loc-" then a UUID. Required if account_id is blank.

location_name
string

Location Name. Can be used instead of the Location ID if Account ID is present.

first_name
string

First Name of the customer

last_name
string
email
string <email>

Required if customer_phone is blank.

phone_number
string^\\+[1-9]\\d{1,14}$

Required if customer_email is blank.

lead_source
string or null (NormalizedLeadSource)
Enum: "google" "facebook" "yelp" "bbb" "yellow pages" "angi" "homeadvisor" "nextdoor" "houzz" "trustpilot" "referral" "organic search" "direct" "email" "print" "broadcast" "event" "instagram" "linkedin" "twitter" "tiktok" "craigslist" "thumbtack" "porch"

The canonical lead source value. Use these standardized values when creating or updating customers. These values are used for lead source-based review routing to match customers to appropriate review channels.

Review Channel Platforms (map to specific review channels):

  • google: Google Business Profile (GBP), Google Maps, Google Search, Google Ads
  • facebook: Facebook Page, Facebook Ads, Meta
  • yelp: Yelp Business Page, Yelp Ads
  • bbb: Better Business Bureau
  • yellow pages: Yellow Pages, YP.com
  • angi: Angi, Angie's List
  • homeadvisor: HomeAdvisor
  • nextdoor: Nextdoor
  • houzz: Houzz
  • trustpilot: Trustpilot

Generic Sources (do not map to specific review channels):

  • referral: Word of mouth, customer/employee/partner referrals
  • organic search: SEO, organic search traffic
  • direct: Direct website traffic, typed URL
  • email: Email marketing, newsletters
  • print: Print ads, newspaper, magazine, direct mail
  • broadcast: Radio, TV advertising
  • event: Trade shows, conferences, expos
  • instagram: Instagram, Instagram Ads
  • linkedin: LinkedIn, LinkedIn Ads
  • twitter: Twitter/X
  • tiktok: TikTok
  • craigslist: Craigslist
  • thumbtack: Thumbtack
  • porch: Porch.com
text_opt_in
boolean
Default: true

Does the customer opt into receiving text messages

object (CustomerCustomFields)

Custom Fields in a object of name: value

status
string
Default: null
Value: "unsubscribed"

Customer status. If "unsubscribed", the customer will be fully unsubscribed from all categories. This takes precedence over unsubscribe_categories. Other status types will trigger an unauthorized error.

unsubscribe_categories
Array of strings (UnsubscribeCategory)
Items Enum: "feedback_informational" "marketing" "transactional" "all"

Categories to unsubscribe the customer from. Use "all" to unsubscribe from all categories. Only applies if status is not "unsubscribed".

Responses

Request samples

Content type
application/json
{
  • "account_id": "string",
  • "location_id": "string",
  • "location_name": "string",
  • "first_name": "string",
  • "last_name": "string",
  • "email": "[email protected]",
  • "phone_number": "+15555555555",
  • "lead_source": "google",
  • "text_opt_in": true,
  • "custom_fields": {
    • "campaign": "string",
    • "parent_company": "string",
    • "address_street": "string",
    • "age": 0,
    • "business_unit": "string",
    • "crm": "string",
    • "custom_Address": "string",
    • "custom_CustomTextWithInstructions": "string",
    • "custom_Date": "2019-08-24",
    • "custom_DateAndTime": "2019-08-24T14:15:22Z",
    • "customer_id": "string",
    • "customer_review": true,
    • "custom_LocationName": "string",
    • "custom_ReservationType": "string",
    • "custom_service_email": "[email protected]",
    • "employee": [
      • "string"
      ],
    • "gender": "string",
    • "income": 0,
    • "invoice_amount": 0,
    • "job_id": "string",
    • "job_status": "string",
    • "job_type": "string",
    • "lead_source": "string",
    • "lifetime_spend": 0,
    • "nps_score": 0,
    • "phone_number": "string",
    • "race": "string",
    • "repeat": true,
    • "tags": [
      • "string"
      ]
    },
  • "status": "unsubscribed",
  • "unsubscribe_categories": [
    • "feedback_informational"
    ]
}

Response samples

Content type
application/json
{
  • "account_id": "string",
  • "location_id": "string",
  • "status": "success",
  • "message": "string"
}

Retrieve a single customer

Retrieve a customer by their unique customer ID. The customer must belong to your account.

Expandable fields: customer_campaigns, reviews

Authorizations:
BearerAuth
path Parameters
customer_id
required
string

The customer ID in the format of "cus-" followed by a UUID.

query Parameters
expand
Array of strings

Expand related objects inline instead of returning IDs. Pass one or more association names to include the full object in the response. Use dot-notation for nested expansion (e.g., expand[]=customer_campaigns.location).

Limits: Maximum 10 expand paths per request, maximum 3 levels of nesting depth.

Without expand: Related objects are either omitted or rendered as compact inline objects (e.g., { "location_id": "loc-...", "name": "..." }).

With expand: Related objects are rendered as full API objects with all their fields.

Error handling: Invalid expand paths return a 400 Bad Request with a message listing valid expansion options.

Responses

Response Schema: application/json
customer_id
string

Customer ID starts with "cus-" then a UUID. Required if account_id is blank.

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

Array of objects
first_name
string

Customer's first name

last_name
string

Customer's last name

email
string <email>

Customer's email address

phone_number
string^\\+[1-9]\\d{1,14}$

A phone number in the E.144 format

lead_source
string or null (NormalizedLeadSource)
Enum: "google" "facebook" "yelp" "bbb" "yellow pages" "angi" "homeadvisor" "nextdoor" "houzz" "trustpilot" "referral" "organic search" "direct" "email" "print" "broadcast" "event" "instagram" "linkedin" "twitter" "tiktok" "craigslist" "thumbtack" "porch"

The canonical lead source value. Use these standardized values when creating or updating customers. These values are used for lead source-based review routing to match customers to appropriate review channels.

Review Channel Platforms (map to specific review channels):

  • google: Google Business Profile (GBP), Google Maps, Google Search, Google Ads
  • facebook: Facebook Page, Facebook Ads, Meta
  • yelp: Yelp Business Page, Yelp Ads
  • bbb: Better Business Bureau
  • yellow pages: Yellow Pages, YP.com
  • angi: Angi, Angie's List
  • homeadvisor: HomeAdvisor
  • nextdoor: Nextdoor
  • houzz: Houzz
  • trustpilot: Trustpilot

Generic Sources (do not map to specific review channels):

  • referral: Word of mouth, customer/employee/partner referrals
  • organic search: SEO, organic search traffic
  • direct: Direct website traffic, typed URL
  • email: Email marketing, newsletters
  • print: Print ads, newspaper, magazine, direct mail
  • broadcast: Radio, TV advertising
  • event: Trade shows, conferences, expos
  • instagram: Instagram, Instagram Ads
  • linkedin: LinkedIn, LinkedIn Ads
  • twitter: Twitter/X
  • tiktok: TikTok
  • craigslist: Craigslist
  • thumbtack: Thumbtack
  • porch: Porch.com
normalized_lead_source
string or null (NormalizedLeadSource)
Enum: "google" "facebook" "yelp" "bbb" "yellow pages" "angi" "homeadvisor" "nextdoor" "houzz" "trustpilot" "referral" "organic search" "direct" "email" "print" "broadcast" "event" "instagram" "linkedin" "twitter" "tiktok" "craigslist" "thumbtack" "porch"

The canonical lead source value. Use these standardized values when creating or updating customers. These values are used for lead source-based review routing to match customers to appropriate review channels.

Review Channel Platforms (map to specific review channels):

  • google: Google Business Profile (GBP), Google Maps, Google Search, Google Ads
  • facebook: Facebook Page, Facebook Ads, Meta
  • yelp: Yelp Business Page, Yelp Ads
  • bbb: Better Business Bureau
  • yellow pages: Yellow Pages, YP.com
  • angi: Angi, Angie's List
  • homeadvisor: HomeAdvisor
  • nextdoor: Nextdoor
  • houzz: Houzz
  • trustpilot: Trustpilot

Generic Sources (do not map to specific review channels):

  • referral: Word of mouth, customer/employee/partner referrals
  • organic search: SEO, organic search traffic
  • direct: Direct website traffic, typed URL
  • email: Email marketing, newsletters
  • print: Print ads, newspaper, magazine, direct mail
  • broadcast: Radio, TV advertising
  • event: Trade shows, conferences, expos
  • instagram: Instagram, Instagram Ads
  • linkedin: LinkedIn, LinkedIn Ads
  • twitter: Twitter/X
  • tiktok: TikTok
  • craigslist: Craigslist
  • thumbtack: Thumbtack
  • porch: Porch.com
lead_source_channel_type
string or null

The review channel type name (e.g., "Google", "Yelp") if the lead source maps to a review channel

status
string
Enum: "active" "unsubscribed"

Customer status

text_opt_in
boolean

Whether customer has opted in to receive text messages

created_at
string <date-time>

When the customer was created

updated_at
string <date-time>

When the customer was last updated

Array of objects (CustomField)
tags
Array of strings

Names of tags applied to this customer.

last_activity_at
string <date-time>

The most recent activity timestamp for the customer

Array of integers or Array of CustomerCampaignItem (objects)

Customer's campaign enrollments. Only present when expand options are used in the request. Collapsed (expand options present but customer_campaigns not expanded): Array of customer_campaign IDs (integers). Expanded (expand[]=customer_campaigns): Array of full campaign objects matching the CustomerCampaignItem schema. Omitted when no expand options are present in the request.

Array of strings or Array of Review (objects)

Confirmed review matches for this customer. Only present when expand options are used in the request. Collapsed (expand options present but reviews not expanded): Array of review_uuid strings (e.g., ["rev-xxx", "rev-yyy"]). Expanded (expand[]=reviews): Array of full review objects matching the Review schema. Omitted when no expand options are present in the request.

Response samples

Content type
application/json
{
  • "customer_id": "cus-550e8400-e29b-41d4-a716-446655440000",
  • "first_name": "Jane",
  • "last_name": "Smith",
  • "email": "[email protected]",
  • "phone_number": "+15551234568",
  • "lead_source": "google",
  • "normalized_lead_source": "google",
  • "lead_source_channel_type": "Google",
  • "status": "active",
  • "text_opt_in": true,
  • "created_at": "2025-08-15T16:37:20Z",
  • "updated_at": "2025-08-16T08:50:18Z",
  • "last_activity_at": "2025-08-16T08:50:18Z",
  • "account_id": "acc-test-402efb14-f779-4dfe-9a07-feb72602c8bb",
  • "locations": [
    • {
      • "location_id": "loc-test-8c0a8daa-dbd8-4e7c-bd38-d810795512e3",
      • "name": "Test Location"
      }
    ],
  • "custom_fields": [
    • {
      • "name": "campaign",
      • "value": "summer_promotion",
      • "label": "Campaign",
      • "type": "text"
      }
    ]
}

Retrieve campaigns for a customer

Retrieve a paginated list of campaigns associated with a customer, including their status.

Authorizations:
BearerAuth
path Parameters
customer_id
required
string

The customer ID in the format of "cus-" followed by a UUID.

query Parameters
page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of campaigns for the customer

total_pages
integer <int32>

Total number of pages available

current_page
integer <int8>

Current page number

Array of objects (CustomerCampaignItem)

Response samples

Content type
application/json
{
  • "total_count": 0,
  • "total_pages": 0,
  • "current_page": 0,
  • "campaigns": [
    • {
      • "customer_campaign_id": 0,
      • "campaign_template_id": 0,
      • "campaign_name": "string",
      • "campaign_type": "reviews",
      • "status": "not_started",
      • "active": true,
      • "goal_met": true,
      • "location": {
        • "location_id": "string",
        • "name": "string"
        },
      • "enrolled_at": "2019-08-24T14:15:22Z",
      • "started_at": "2019-08-24T14:15:22Z",
      • "completed_at": "2019-08-24T14:15:22Z",
      • "exited_at": "2019-08-24T14:15:22Z",
      • "exit_reason": "string",
      • "created_at": "2019-08-24T14:15:22Z",
      • "updated_at": "2019-08-24T14:15:22Z"
      }
    ]
}

Retrieve a single customer campaign

Retrieve a single customer campaign by its unique customer campaign ID. The customer campaign must belong to your account.

Authorizations:
BearerAuth
path Parameters
customer_id
required
string

The customer ID in the format of "cus-" followed by a UUID.

customer_campaign_id
required
number <int64>

The customer campaign ID

Responses

Response Schema: application/json
customer_campaign_id
number <int64>

Campaign template ID

campaign_template_id
number <int64>

Campaign template ID

campaign_name
string

Name of the campaign

campaign_type
string
Enum: "reviews" "informational" "promotional" "transactional"

Type of campaign

status
string
Enum: "not_started" "started" "paused" "complete" "cancelled"

Current status of the customer's campaign

active
boolean

Whether the campaign is currently active

goal_met
boolean

Whether the campaign goal was met

object

Location associated with this campaign

enrolled_at
string or null <date-time>

When the customer was enrolled in the campaign

started_at
string or null <date-time>

When the campaign started for this customer

completed_at
string or null <date-time>

When the campaign was completed

exited_at
string or null <date-time>

When the customer exited the campaign

exit_reason
string or null

Reason for exiting the campaign

created_at
string <date-time>

When the campaign record was created

updated_at
string <date-time>

When the campaign record was last updated

Response samples

Content type
application/json
{
  • "customer_campaign_id": 0,
  • "campaign_template_id": 0,
  • "campaign_name": "string",
  • "campaign_type": "reviews",
  • "status": "not_started",
  • "active": true,
  • "goal_met": true,
  • "location": {
    • "location_id": "string",
    • "name": "string"
    },
  • "enrolled_at": "2019-08-24T14:15:22Z",
  • "started_at": "2019-08-24T14:15:22Z",
  • "completed_at": "2019-08-24T14:15:22Z",
  • "exited_at": "2019-08-24T14:15:22Z",
  • "exit_reason": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

Retrieve touchpoints for a customer campaign

Retrieve a paginated list of touchpoints associated with a customer campaign, including their status. There is a hard limit of 100 touchpoints returned.

Authorizations:
BearerAuth
path Parameters
customer_id
required
string

The customer ID in the format of "cus-" followed by a UUID.

customer_campaign_id
required
number <int64>

The customer campaign ID

Responses

Response Schema: application/json
status
string
Enum: "scheduled" "queued" "sent" "delivered" "failed" "cancelled" "deferred"

Current status of the touchpoint

sequence_order
number <int64>

Sequence order of the touchpoint

days_after_previous
number <int64>

Number of days after the previous touchpoint

scheduled_for
string <date-time>

When the touchpoint is scheduled to be sent

sent_at
string or null <date-time>

When the touchpoint was sent

channel
string
Enum: "email" "sms"

Channel for the touchpoint

delivered_at
string or null <date-time>

When the touchpoint was delivered

has_error
boolean

Whether the touchpoint has an error

error_message
string or null

Error message for the touchpoint

Response samples

Content type
application/json
{
  • "status": "scheduled",
  • "sequence_order": 0,
  • "days_after_previous": 0,
  • "scheduled_for": "2019-08-24T14:15:22Z",
  • "sent_at": "2019-08-24T14:15:22Z",
  • "channel": "email",
  • "delivered_at": "2019-08-24T14:15:22Z",
  • "has_error": true,
  • "error_message": "string"
}

Unsubscribe a Customer

Unsubscribe a Customer

Authorizations:
BearerAuth
path Parameters
customer_id
required
string

A UUID prefixed with "cus-"

Request Body schema: application/json
optional

Optional categories to unsubscribe from. If not provided, unsubscribes from all categories.

unsubscribe_categories
Array of strings (UnsubscribeCategory)
Items Enum: "feedback_informational" "marketing" "transactional" "all"

Categories to unsubscribe from. Use "all" or omit this field to unsubscribe from all categories.

Responses

Response Schema: application/json
status
string
Default: "success"
Enum: "success" "error"
message
string

Details about what categories were unsubscribed

Request samples

Content type
application/json
{
  • "unsubscribe_categories": [
    • "feedback_informational"
    ]
}

Response samples

Content type
application/json
{
  • "status": "success",
  • "message": "string"
}

Import a customer to a Campaign

Import a customer to a Campaign

Authorizations:
BearerAuth
Request Body schema: application/json
Any of
account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

location_id
required
string

Location ID starts with "loc-" then a UUID. Required if account_id is blank.

location_name
string

Location Name. Can be used instead of the Location ID if Account ID is present.

customer_first_name
string

First Name of the customer

customer_last_name
string
customer_email
string <email>

Required if customer_phone is blank.

customer_phone
string^\\+[1-9]\\d{1,14}$

Required if customer_email is blank.

lead_source
string or null (NormalizedLeadSource)
Enum: "google" "facebook" "yelp" "bbb" "yellow pages" "angi" "homeadvisor" "nextdoor" "houzz" "trustpilot" "referral" "organic search" "direct" "email" "print" "broadcast" "event" "instagram" "linkedin" "twitter" "tiktok" "craigslist" "thumbtack" "porch"

The canonical lead source value. Use these standardized values when creating or updating customers. These values are used for lead source-based review routing to match customers to appropriate review channels.

Review Channel Platforms (map to specific review channels):

  • google: Google Business Profile (GBP), Google Maps, Google Search, Google Ads
  • facebook: Facebook Page, Facebook Ads, Meta
  • yelp: Yelp Business Page, Yelp Ads
  • bbb: Better Business Bureau
  • yellow pages: Yellow Pages, YP.com
  • angi: Angi, Angie's List
  • homeadvisor: HomeAdvisor
  • nextdoor: Nextdoor
  • houzz: Houzz
  • trustpilot: Trustpilot

Generic Sources (do not map to specific review channels):

  • referral: Word of mouth, customer/employee/partner referrals
  • organic search: SEO, organic search traffic
  • direct: Direct website traffic, typed URL
  • email: Email marketing, newsletters
  • print: Print ads, newspaper, magazine, direct mail
  • broadcast: Radio, TV advertising
  • event: Trade shows, conferences, expos
  • instagram: Instagram, Instagram Ads
  • linkedin: LinkedIn, LinkedIn Ads
  • twitter: Twitter/X
  • tiktok: TikTok
  • craigslist: Craigslist
  • thumbtack: Thumbtack
  • porch: Porch.com
text_opt_in
boolean
Default: true

Does the customer opt into receiving text messages

status
string
Value: "unsubscribed"

Customer status. If "unsubscribed", takes precedence over unsubscribe_categories and unsubscribes from all categories.

unsubscribe_categories
Array of strings (UnsubscribeCategory)
Items Enum: "feedback_informational" "marketing" "transactional" "all"

Categories to unsubscribe the customer from. Use "all" to unsubscribe from all categories. Only applies if status is not "unsubscribed".

object (CustomerCustomFields)

Custom Fields in a object of name: value.

Unknown field names are automatically registered as new text custom fields for the account when the account's custom-field auto-registration setting is enabled. Auto-registered names must be snake_case: starting with a lowercase letter or underscore and containing only lowercase letters, numbers, and underscores. When auto-registration is disabled (the default) or the name is not snake_case, unknown field names are ignored.

Responses

Request samples

Content type
application/json
{
  • "account_id": "string",
  • "location_id": "string",
  • "location_name": "string",
  • "customer_first_name": "string",
  • "customer_last_name": "string",
  • "customer_email": "[email protected]",
  • "customer_phone": "+15555555555",
  • "lead_source": "google",
  • "text_opt_in": true,
  • "status": "unsubscribed",
  • "unsubscribe_categories": [
    • "feedback_informational"
    ],
  • "custom_fields": {
    • "campaign": "string",
    • "parent_company": "string",
    • "address_street": "string",
    • "age": 0,
    • "business_unit": "string",
    • "crm": "string",
    • "custom_Address": "string",
    • "custom_CustomTextWithInstructions": "string",
    • "custom_Date": "2019-08-24",
    • "custom_DateAndTime": "2019-08-24T14:15:22Z",
    • "customer_id": "string",
    • "customer_review": true,
    • "custom_LocationName": "string",
    • "custom_ReservationType": "string",
    • "custom_service_email": "[email protected]",
    • "employee": [
      • "string"
      ],
    • "gender": "string",
    • "income": 0,
    • "invoice_amount": 0,
    • "job_id": "string",
    • "job_status": "string",
    • "job_type": "string",
    • "lead_source": "string",
    • "lifetime_spend": 0,
    • "nps_score": 0,
    • "phone_number": "string",
    • "race": "string",
    • "repeat": true,
    • "tags": [
      • "string"
      ]
    }
}

Response samples

Content type
application/json
{
  • "status": "success",
  • "sync_id": "string"
}

Get the status of an import

Get the status of an import

Authorizations:
BearerAuth
path Parameters
sync_id
required
string

Responses

Response Schema: application/json
One of
sync_id
string

The sync ID that starts with "sync-"

status
string

The current status of the import

customer_id
string

Customer ID starts with "cus-" then a UUID. Required if account_id is blank.

location_id
string

Location ID starts with "loc-" then a UUID. Required if account_id is blank.

campaign_ids
Array of numbers <int64> [ items <int64 > ]

Array of campaign IDs created within 1 hour of the sync

Response samples

Content type
application/json
Example
{
  • "sync_id": "sync-550e8400-e29b-41d4-a716-446655440000",
  • "status": "completed",
  • "customer_id": "cus-660e8400-e29b-41d4-a716-446655440001",
  • "location_id": "loc-test-8c0a8daa-dbd8-4e7c-bd38-d810795512e3",
  • "campaign_ids": [
    • 123,
    • 456
    ]
}

Conversations

Access customer conversations and conversation messages

List conversations

Retrieve a paginated list of conversations for the authenticated account.

Conversations are scoped to the account attached to the Bearer token. The response intentionally omits account_id, provider identifiers, per-message source metadata, and multi-value campaign/channel arrays.

campaign is the latest campaign-sourced message's campaign template, or null when the conversation has no campaign-sourced message. campaign_template_id filters and responses use public cmp-* IDs.

Expandable fields: customer, location

When expanded, customer includes the full customer object with all fields. When not expanded, customer is a compact object with customer_id, first_name, and last_name.

When expanded, location includes the full location object with address, city, state, zipcode, country, phone number, timezone, and custom fields. When not expanded, location is a compact object with location_id and name.

Authorizations:
BearerAuth
query Parameters
string or Array of strings

Filter conversations by status. Accepts one or more values; invalid values return a 400 response.

string or Array of strings

The location ID in the format of "loc-" followed by the UUID.

campaign_template_id
string^cmp-[A-Za-z0-9-]+$
Example: campaign_template_id=cmp-abc123

Filter conversations by the public campaign template ID in the format cmp-*.

channel_type
string
Enum: "sms" "whatsapp" "email"

Filter conversations that have at least one message on this API channel type.

created_at_gt
string <date-time>

Return conversations created strictly after this ISO 8601 timestamp. Ignored when created_at_eq is present.

created_at_gte
string <date-time>

Return conversations created at or after this ISO 8601 timestamp. Ignored when created_at_eq is present.

created_at_lt
string <date-time>

Return conversations created strictly before this ISO 8601 timestamp. Ignored when created_at_eq is present.

created_at_lte
string <date-time>

Return conversations created at or before this ISO 8601 timestamp. Ignored when created_at_eq is present.

created_at_eq
string <date-time>

Return conversations created on the same date or instant as this ISO 8601 timestamp. When present, range filters for created_at are ignored.

last_message_at_gt
string <date-time>

Return conversations whose latest message was sent strictly after this ISO 8601 timestamp. Ignored when last_message_at_eq is present.

last_message_at_gte
string <date-time>

Return conversations whose latest message was sent at or after this ISO 8601 timestamp. Ignored when last_message_at_eq is present.

last_message_at_lt
string <date-time>

Return conversations whose latest message was sent strictly before this ISO 8601 timestamp. Ignored when last_message_at_eq is present.

last_message_at_lte
string <date-time>

Return conversations whose latest message was sent at or before this ISO 8601 timestamp. Ignored when last_message_at_eq is present.

last_message_at_eq
string <date-time>

Return conversations whose latest message date or instant matches this ISO 8601 timestamp. When present, range filters for last_message_at are ignored.

expand
Array of strings

Expand related objects inline instead of returning IDs. Pass one or more association names to include the full object in the response. Use dot-notation for nested expansion (e.g., expand[]=customer_campaigns.location).

Limits: Maximum 10 expand paths per request, maximum 3 levels of nesting depth.

Without expand: Related objects are either omitted or rendered as compact inline objects (e.g., { "location_id": "loc-...", "name": "..." }).

With expand: Related objects are rendered as full API objects with all their fields.

Error handling: Invalid expand paths return a 400 Bad Request with a message listing valid expansion options.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

Responses

Response Schema: application/json
total_count
required
integer <int32>

Total number of conversations matching the filter criteria

total_pages
required
integer <int32>

Total number of pages available

current_page
required
integer <int32>

Current page number

required
Array of objects (ConversationListItem)

Response samples

Content type
application/json
{
  • "total_count": 150,
  • "total_pages": 2,
  • "current_page": 1,
  • "conversations": [
    • {
      • "conversation_id": 123,
      • "status": "open",
      • "customer": {
        • "customer_id": "cus-abc123",
        • "first_name": "John",
        • "last_name": "Doe"
        },
      • "location": {
        • "location_id": "loc-abc123",
        • "name": "Main Street"
        },
      • "campaign": {
        • "campaign_template_id": "cmp-abc123",
        • "campaign_name": "Follow Up Campaign"
        },
      • "last_channel_type": "sms",
      • "message_count": 5,
      • "last_message_at": "2026-03-12T15:30:00Z",
      • "last_message_preview": "Thanks for reaching out...",
      • "created_at": "2026-03-10T10:00:00Z",
      • "updated_at": "2026-03-12T15:30:00Z"
      }
    ]
}

List conversations for a customer

Retrieve a paginated list of conversations for a single customer in the authenticated account.

The response keeps the same conversation contract as GET /conversations, except the top-level customer_id identifies the requested customer and each conversation item omits the embedded customer object.

Expandable fields: location

When expanded, location includes the full location object with address, city, state, zipcode, country, phone number, timezone, and custom fields. When not expanded, location is a compact object with location_id and name.

Because each item omits the embedded customer object, expand[]=customer is accepted but ignored on this endpoint.

Authorizations:
BearerAuth
path Parameters
customer_id
required
string

The customer ID in the format of "cus-" followed by a UUID.

query Parameters
string or Array of strings

Filter conversations by status. Accepts one or more values; invalid values return a 400 response.

string or Array of strings

The location ID in the format of "loc-" followed by the UUID.

campaign_template_id
string^cmp-[A-Za-z0-9-]+$
Example: campaign_template_id=cmp-abc123

Filter conversations by the public campaign template ID in the format cmp-*.

channel_type
string
Enum: "sms" "whatsapp" "email"

Filter conversations that have at least one message on this API channel type.

created_at_gt
string <date-time>

Return conversations created strictly after this ISO 8601 timestamp. Ignored when created_at_eq is present.

created_at_gte
string <date-time>

Return conversations created at or after this ISO 8601 timestamp. Ignored when created_at_eq is present.

created_at_lt
string <date-time>

Return conversations created strictly before this ISO 8601 timestamp. Ignored when created_at_eq is present.

created_at_lte
string <date-time>

Return conversations created at or before this ISO 8601 timestamp. Ignored when created_at_eq is present.

created_at_eq
string <date-time>

Return conversations created on the same date or instant as this ISO 8601 timestamp. When present, range filters for created_at are ignored.

last_message_at_gt
string <date-time>

Return conversations whose latest message was sent strictly after this ISO 8601 timestamp. Ignored when last_message_at_eq is present.

last_message_at_gte
string <date-time>

Return conversations whose latest message was sent at or after this ISO 8601 timestamp. Ignored when last_message_at_eq is present.

last_message_at_lt
string <date-time>

Return conversations whose latest message was sent strictly before this ISO 8601 timestamp. Ignored when last_message_at_eq is present.

last_message_at_lte
string <date-time>

Return conversations whose latest message was sent at or before this ISO 8601 timestamp. Ignored when last_message_at_eq is present.

last_message_at_eq
string <date-time>

Return conversations whose latest message date or instant matches this ISO 8601 timestamp. When present, range filters for last_message_at are ignored.

expand
Array of strings

Expand related objects inline instead of returning IDs. Pass one or more association names to include the full object in the response. Use dot-notation for nested expansion (e.g., expand[]=customer_campaigns.location).

Limits: Maximum 10 expand paths per request, maximum 3 levels of nesting depth.

Without expand: Related objects are either omitted or rendered as compact inline objects (e.g., { "location_id": "loc-...", "name": "..." }).

With expand: Related objects are rendered as full API objects with all their fields.

Error handling: Invalid expand paths return a 400 Bad Request with a message listing valid expansion options.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

Responses

Response Schema: application/json
customer_id
required
string

Customer ID starts with "cus-" then a UUID. Required if account_id is blank.

total_count
required
integer <int32>

Total number of conversations for the customer matching the filter criteria

total_pages
required
integer <int32>

Total number of pages available

current_page
required
integer <int32>

Current page number

required
Array of objects (CustomerConversationListItem)

Response samples

Content type
application/json
{
  • "customer_id": "cus-abc123",
  • "total_count": 3,
  • "total_pages": 1,
  • "current_page": 1,
  • "conversations": [
    • {
      • "conversation_id": 123,
      • "status": "open",
      • "location": {
        • "location_id": "loc-abc123",
        • "name": "Main Street"
        },
      • "campaign": {
        • "campaign_template_id": "cmp-abc123",
        • "campaign_name": "Follow Up Campaign"
        },
      • "last_channel_type": "sms",
      • "message_count": 5,
      • "last_message_at": "2026-03-12T15:30:00Z",
      • "last_message_preview": "Thanks for reaching out...",
      • "created_at": "2026-03-10T10:00:00Z",
      • "updated_at": "2026-03-12T15:30:00Z"
      }
    ]
}

List messages for a conversation

Retrieve a paginated list of messages for a customer's conversation.

Message body is plaintext. Email HTML bodies are converted to plaintext before serialization. read_at is the Liftify internal inbox read state, not an external provider or customer read receipt. Provider IDs such as Twilio SIDs and provider message IDs are intentionally omitted.

Authorizations:
BearerAuth
path Parameters
customer_id
required
string

The customer ID in the format of "cus-" followed by a UUID.

conversation_id
required
integer <int64>

Numeric conversation identifier from the conversations list response.

query Parameters
page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

Responses

Response Schema: application/json
customer_id
required
string

Customer ID starts with "cus-" then a UUID. Required if account_id is blank.

conversation_id
required
integer <int64>

Numeric conversation identifier from the conversations list response

total_count
required
integer <int32>

Total number of messages in the conversation

total_pages
required
integer <int32>

Total number of pages available

current_page
required
integer <int32>

Current page number

required
Array of objects (ConversationMessage)

Response samples

Content type
application/json
{
  • "customer_id": "cus-abc123",
  • "conversation_id": 123,
  • "total_count": 12,
  • "total_pages": 1,
  • "current_page": 1,
  • "messages": [
    • {
      • "message_id": 789,
      • "direction": "outbound",
      • "body": "Hi John, how was your experience?",
      • "channel_type": "sms",
      • "status": "delivered",
      • "sent_at": "2026-03-10T10:00:00Z",
      • "delivered_at": "2026-03-10T10:00:05Z",
      • "read_at": null,
      • "created_at": "2026-03-10T10:00:00Z"
      }
    ]
}

Surveys

Access surveys, versions, questions, and responses

Retrieve surveys

Retrieve a paginated list of surveys for the authenticated account. Use the optional status filter to retrieve surveys in a specific state.

Expandable fields: versions, versions.questions

When expanded, versions includes the full survey version objects with version number, provider, and timestamps. When not expanded, versions returns an array of version public_id strings.

When versions.questions is expanded, each version includes the full question objects with question key, type, text, metadata, and scoring information. When not expanded, questions returns an array of question public_id strings within each version.

Authorizations:
BearerAuth
query Parameters
status
string
Enum: "draft" "active" "archived"

Filter surveys by status.

expand
Array of strings

Expand related objects inline instead of returning IDs. Pass one or more association names to include the full object in the response. Use dot-notation for nested expansion (e.g., expand[]=customer_campaigns.location).

Limits: Maximum 10 expand paths per request, maximum 3 levels of nesting depth.

Without expand: Related objects are either omitted or rendered as compact inline objects (e.g., { "location_id": "loc-...", "name": "..." }).

With expand: Related objects are rendered as full API objects with all their fields.

Error handling: Invalid expand paths return a 400 Bad Request with a message listing valid expansion options.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

created_at_gt
string <date-time>

Return surveys with a created_at strictly greater than this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

created_at_gte
string <date-time>

Return surveys with a created_at greater than or equal to this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

created_at_lt
string <date-time>

Return surveys with a created_at strictly less than this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

created_at_lte
string <date-time>

Return surveys with a created_at less than or equal to this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

string or string

Filter surveys created on the specified date, ignoring the time portion. Accepts either an ISO-8601 date (YYYY-MM-DD) or a full ISO-8601 date-time; only the date portion is used for filtering.

Precedence: When created_at_eq is provided, all other date-time filters (created_at_gt, created_at_gte, created_at_lt, created_at_lte) are ignored.

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of surveys matching the filter criteria

total_pages
integer <int32>

Total number of pages available

current_page
integer <int8>

Current page number

Array of objects (SurveyItem)

Response samples

Content type
application/json
{
  • "total_count": 5,
  • "total_pages": 1,
  • "current_page": 1,
  • "surveys": [
    • {
      • "public_id": "550e8400-e29b-41d4-a716-446655440000",
      • "name": "Customer Satisfaction Survey",
      • "status": "active",
      • "provider": "liftify",
      • "created_at": "2025-08-15T16:37:20Z",
      • "updated_at": "2025-08-16T08:50:18Z",
      • "versions": [
        • "660e8400-e29b-41d4-a716-446655440001"
        ]
      }
    ]
}

Retrieve a single survey

Retrieve a single survey by its public UUID. The survey must belong to your account.

Expandable fields: versions, versions.questions

When expanded, versions includes the full survey version objects. When not expanded, versions returns an array of version public_id strings.

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>

The survey public ID (UUID).

query Parameters
expand
Array of strings

Expand related objects inline instead of returning IDs. Pass one or more association names to include the full object in the response. Use dot-notation for nested expansion (e.g., expand[]=customer_campaigns.location).

Limits: Maximum 10 expand paths per request, maximum 3 levels of nesting depth.

Without expand: Related objects are either omitted or rendered as compact inline objects (e.g., { "location_id": "loc-...", "name": "..." }).

With expand: Related objects are rendered as full API objects with all their fields.

Error handling: Invalid expand paths return a 400 Bad Request with a message listing valid expansion options.

Responses

Response Schema: application/json
public_id
string <uuid>

Unique public identifier for the survey (UUID)

name
string

Survey name

status
string
Enum: "draft" "active" "archived"

Current status of the survey

provider
string
Enum: "liftify" "birdeye"

Survey provider

Array of strings or Array of SurveyVersionItem (objects)

Survey versions. Always present in the response. Collapsed (default or versions not expanded): Array of version public_id strings (e.g., ["uuid-1", "uuid-2"]). Expanded (expand[]=versions): Array of full SurveyVersionItem objects.

created_at
string <date-time>

When the survey was created

updated_at
string <date-time>

When the survey was last updated

Response samples

Content type
application/json
{
  • "public_id": "550e8400-e29b-41d4-a716-446655440000",
  • "name": "Customer Satisfaction Survey",
  • "status": "active",
  • "provider": "liftify",
  • "created_at": "2025-08-15T16:37:20Z",
  • "updated_at": "2025-08-16T08:50:18Z",
  • "versions": [
    • "660e8400-e29b-41d4-a716-446655440001"
    ]
}

Retrieve responses for a survey

Retrieve a paginated list of responses for a specific survey. The survey must belong to your account.

Expandable fields: answers, customer, location, survey, survey_version, survey_version.questions

Includable fields: customer_snapshot

When included (include[]=customer_snapshot), each response carries the raw contact snapshot captured when it was ingested or submitted, exactly as stored — contact details plus the original inbound contact payload (original_contact) with custom fields keyed by the labels they arrived with. Values reflect survey time, independent of later edits to the customer record. Include is purely additive: no default fields are removed or reshaped, and it composes with expand. When not requested, the field is omitted.

When expanded, answers includes the full answer objects with question key, question text, answer value, question type, and conditional flags. When not expanded (but expand options are present), answers returns an array of answer public_id strings. When no expand options are present, the answers field is omitted.

When expanded, customer includes the full customer object. When not expanded, customer_id is returned as a string (nullable).

When expanded, location includes the full location object. When not expanded, location_id is returned as a string.

When expanded, survey includes the full survey object. When not expanded, survey_id is returned as a string.

When expanded, survey_version includes the full survey version object. When not expanded, survey_version_id is returned as a string. Use survey_version.questions to also expand questions within the version.

Authorizations:
BearerAuth
path Parameters
survey_id
required
string <uuid>

The survey public ID (UUID).

query Parameters
completion_status
string
Enum: "pending" "partial" "complete"

Filter responses by completion status.

string or Array of strings

The location ID in the format of "loc-" followed by the UUID.

expand
Array of strings

Expand related objects inline instead of returning IDs. Pass one or more association names to include the full object in the response. Use dot-notation for nested expansion (e.g., expand[]=customer_campaigns.location).

Limits: Maximum 10 expand paths per request, maximum 3 levels of nesting depth.

Without expand: Related objects are either omitted or rendered as compact inline objects (e.g., { "location_id": "loc-...", "name": "..." }).

With expand: Related objects are rendered as full API objects with all their fields.

Error handling: Invalid expand paths return a 400 Bad Request with a message listing valid expansion options.

include
Array of strings

Opt into raw fields that are omitted from the response by default (e.g. include[]=customer_snapshot).

Include is purely additive: the default payload is unchanged, and each requested field is added to every response object. It is distinct from expand, which changes the shape of always-present association keys — the two compose freely in one request.

Error handling: Unknown include values return a 400 Bad Request with a message listing valid includes.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

created_at_gt
string <date-time>

Return responses with a created_at strictly greater than this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

created_at_gte
string <date-time>

Return responses with a created_at greater than or equal to this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

created_at_lt
string <date-time>

Return responses with a created_at strictly less than this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

created_at_lte
string <date-time>

Return responses with a created_at less than or equal to this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

string or string

Filter responses created on the specified date, ignoring the time portion.

Precedence: When created_at_eq is provided, all other date-time filters are ignored.

response_date_gt
string <date-time>

Return responses completed strictly after this value. This is an ISO 8601 date-time string. Responses the customer has not completed (null response_date) are always excluded. Ignored when response_date_eq is present.

response_date_gte
string <date-time>

Return responses completed at or after this value. This is an ISO 8601 date-time string. Responses the customer has not completed (null response_date) are always excluded. Ignored when response_date_eq is present.

response_date_lt
string <date-time>

Return responses completed strictly before this value. This is an ISO 8601 date-time string. Responses the customer has not completed (null response_date) are always excluded. Ignored when response_date_eq is present.

response_date_lte
string <date-time>

Return responses completed at or before this value. This is an ISO 8601 date-time string. Responses the customer has not completed (null response_date) are always excluded. Ignored when response_date_eq is present.

string or string

Filter responses completed on the specified date, ignoring the time portion. Responses the customer has not completed (null response_date) are always excluded.

Precedence: When response_date_eq is provided, all other response_date filters are ignored.

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of responses matching the filter criteria

total_pages
integer <int32>

Total number of pages available

current_page
integer <int8>

Current page number

Array of objects (SurveyResponseItem)

Response samples

Content type
application/json
{
  • "total_count": 25,
  • "total_pages": 1,
  • "current_page": 1,
  • "responses": [
    • {
      • "public_id": "770e8400-e29b-41d4-a716-446655440002",
      • "completion_status": "complete",
      • "overall_score": 4.5,
      • "nps_score": 9,
      • "provider": "liftify",
      • "anonymous": false,
      • "customer_display_name": "John D.",
      • "request_date": "2025-08-14T09:00:00Z",
      • "response_date": "2025-08-16T08:50:18Z",
      • "created_at": "2025-08-15T16:37:20Z",
      • "updated_at": "2025-08-16T08:50:18Z"
      }
    ]
}

Bulk import surveys via NDJSON

Import multiple surveys with responses in a single request using NDJSON (Newline-Delimited JSON) format. Each line of the request body must be a valid JSON object containing a survey definition and optional responses.

NDJSON Format: Each line is a separate JSON object. Lines are delimited by newline characters (\n).

Processing: Each line is processed asynchronously by a background worker. Use the returned sync_id values to track the status of each import via the /surveys/import/{sync_id}/status endpoint.

De-duplication: The system automatically detects and skips duplicate payloads within a 10-second window. Fingerprint is generated from: survey external_id, response external_response_id, and contact email/phone. Duplicate responses return skipped status without error.

Survey Versioning: When a survey schema changes (questions added/removed/modified), a new version is automatically created. Existing responses remain linked to their original version.

Authorizations:
BearerAuth
Request Body schema: application/x-ndjson
required

NDJSON format where each line contains a complete survey import payload. Each line must have a survey key with the survey definition.

required
object (SurveyDefinition)

Survey definition containing structure and questions

Array of objects (SurveyResponsePayload)

Optional array of survey responses to import with the survey

Responses

Request samples

Content type
application/x-ndjson
Example
"{\"survey\":{\"external_id\":\"survey_001\",\"name\":\"Customer Satisfaction Survey\",\"provider\":\"birdeye\",\"pages\":[{\"page_key\":\"page_1\",\"page_number\":1,\"questions\":[{\"question_key\":\"q1\",\"text\":\"How satisfied are you?\",\"type\":\"nps\",\"metadata\":{\"promoter_threshold\":9,\"detractor_threshold\":6}}]}]},\"responses\":[{\"contact\":{\"first_name\":\"John\",\"last_name\":\"Doe\",\"email\":\"[email protected]\"},\"location\":{\"external_id\":\"loc_123\"},\"external_response_id\":\"resp_001\",\"response_date\":\"2025-01-15T10:30:00Z\",\"answers\":[{\"question_key\":\"q1\",\"answer_value\":9}]}]}\n"

Response samples

Content type
application/json
{
  • "sync_ids": [
    • "sync-550e8400-e29b-41d4-a716-446655440000",
    • "sync-660e8400-e29b-41d4-a716-446655440001"
    ],
  • "message": "2 surveys queued for import"
}

Get survey import status

Retrieve the status of a survey import operation using the sync_id returned from the import endpoint.

Status Flow:

  • pending: Import is queued and being processed
  • success: Import completed successfully
  • partial: Import completed with some records skipped (e.g., duplicates)
  • error: Import failed with errors

Note: Once a SyncHistory record exists, the pending Redis key is automatically cleaned up.

Authorizations:
BearerAuth
path Parameters
sync_id
required
string
Example: sync-550e8400-e29b-41d4-a716-446655440000

The sync ID returned from the import endpoint (prefixed with "sync-")

Responses

Response Schema: application/json
One of
sync_id
required
string

The sync ID prefixed with "sync-"

status
required
string
Enum: "success" "partial"

The status of the import operation

source
required
string
Value: "survey_import"

The source of the sync operation

action
string

The action performed

location_id
string

Location ID starts with "loc-" then a UUID. Required if account_id is blank.

customer_id
string

Customer ID starts with "cus-" then a UUID. Required if account_id is blank.

object

Additional result data from the import

created_at
required
string <date-time>

When the sync operation was created

updated_at
required
string <date-time>

When the sync operation was last updated

Response samples

Content type
application/json
Example
{
  • "sync_id": "sync-550e8400-e29b-41d4-a716-446655440000",
  • "status": "success",
  • "source": "survey_import",
  • "action": "survey_ingestion",
  • "location_id": "loc-test-8c0a8daa-dbd8-4e7c-bd38-d810795512e3",
  • "customer_id": "cus-660e8400-e29b-41d4-a716-446655440001",
  • "created_at": "2025-01-15T10:30:00Z",
  • "updated_at": "2025-01-15T10:30:05Z",
  • "result": {
    • "surveys_created": 1,
    • "responses_processed": 5
    }
}

Retrieve survey responses

Retrieve a paginated list of survey responses for the authenticated account. At least one filter is required: survey_id or location_id.

Expandable fields: answers, customer, location, survey, survey_version, survey_version.questions

Includable fields: customer_snapshot

See /surveys/{survey_id}/responses for detailed expand and include behavior descriptions.

Authorizations:
BearerAuth
query Parameters
survey_id
string <uuid>

Filter responses by survey public ID (UUID). Required if location_id is not provided.

string or Array of strings

The location ID in the format of "loc-" followed by the UUID.

completion_status
string
Enum: "pending" "partial" "complete"

Filter responses by completion status.

expand
Array of strings

Expand related objects inline instead of returning IDs. Pass one or more association names to include the full object in the response. Use dot-notation for nested expansion (e.g., expand[]=customer_campaigns.location).

Limits: Maximum 10 expand paths per request, maximum 3 levels of nesting depth.

Without expand: Related objects are either omitted or rendered as compact inline objects (e.g., { "location_id": "loc-...", "name": "..." }).

With expand: Related objects are rendered as full API objects with all their fields.

Error handling: Invalid expand paths return a 400 Bad Request with a message listing valid expansion options.

include
Array of strings

Opt into raw fields that are omitted from the response by default (e.g. include[]=customer_snapshot).

Include is purely additive: the default payload is unchanged, and each requested field is added to every response object. It is distinct from expand, which changes the shape of always-present association keys — the two compose freely in one request.

Error handling: Unknown include values return a 400 Bad Request with a message listing valid includes.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

created_at_gt
string <date-time>

Return responses with a created_at strictly greater than this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

created_at_gte
string <date-time>

Return responses with a created_at greater than or equal to this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

created_at_lt
string <date-time>

Return responses with a created_at strictly less than this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

created_at_lte
string <date-time>

Return responses with a created_at less than or equal to this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

string or string

Filter responses created on the specified date, ignoring the time portion.

Precedence: When created_at_eq is provided, all other date-time filters are ignored.

response_date_gt
string <date-time>

Return responses completed strictly after this value. This is an ISO 8601 date-time string. Responses the customer has not completed (null response_date) are always excluded. Ignored when response_date_eq is present.

response_date_gte
string <date-time>

Return responses completed at or after this value. This is an ISO 8601 date-time string. Responses the customer has not completed (null response_date) are always excluded. Ignored when response_date_eq is present.

response_date_lt
string <date-time>

Return responses completed strictly before this value. This is an ISO 8601 date-time string. Responses the customer has not completed (null response_date) are always excluded. Ignored when response_date_eq is present.

response_date_lte
string <date-time>

Return responses completed at or before this value. This is an ISO 8601 date-time string. Responses the customer has not completed (null response_date) are always excluded. Ignored when response_date_eq is present.

string or string

Filter responses completed on the specified date, ignoring the time portion. Responses the customer has not completed (null response_date) are always excluded.

Precedence: When response_date_eq is provided, all other response_date filters are ignored.

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of responses matching the filter criteria

total_pages
integer <int32>

Total number of pages available

current_page
integer <int8>

Current page number

Array of objects (SurveyResponseItem)

Response samples

Content type
application/json
{
  • "total_count": 25,
  • "total_pages": 1,
  • "current_page": 1,
  • "responses": [
    • {
      • "public_id": "770e8400-e29b-41d4-a716-446655440002",
      • "completion_status": "complete",
      • "overall_score": 4.5,
      • "nps_score": 9,
      • "provider": "liftify",
      • "anonymous": false,
      • "customer_display_name": "John D.",
      • "request_date": "2025-08-14T09:00:00Z",
      • "response_date": "2025-08-16T08:50:18Z",
      • "created_at": "2025-08-15T16:37:20Z",
      • "updated_at": "2025-08-16T08:50:18Z"
      }
    ]
}

Retrieve a single survey response

Retrieve a single survey response by its public UUID. The response must belong to your account.

Expandable fields: answers, customer, location, survey, survey_version, survey_version.questions

Includable fields: customer_snapshot

See /surveys/{survey_id}/responses for detailed expand and include behavior descriptions.

Authorizations:
BearerAuth
path Parameters
id
required
string <uuid>

The survey response public ID (UUID).

query Parameters
expand
Array of strings

Expand related objects inline instead of returning IDs. Pass one or more association names to include the full object in the response. Use dot-notation for nested expansion (e.g., expand[]=customer_campaigns.location).

Limits: Maximum 10 expand paths per request, maximum 3 levels of nesting depth.

Without expand: Related objects are either omitted or rendered as compact inline objects (e.g., { "location_id": "loc-...", "name": "..." }).

With expand: Related objects are rendered as full API objects with all their fields.

Error handling: Invalid expand paths return a 400 Bad Request with a message listing valid expansion options.

include
Array of strings

Opt into raw fields that are omitted from the response by default (e.g. include[]=customer_snapshot).

Include is purely additive: the default payload is unchanged, and each requested field is added to every response object. It is distinct from expand, which changes the shape of always-present association keys — the two compose freely in one request.

Error handling: Unknown include values return a 400 Bad Request with a message listing valid includes.

Responses

Response Schema: application/json
public_id
string <uuid>

Unique public identifier for the response (UUID)

completion_status
string
Enum: "pending" "partial" "complete"

Response completion status

overall_score
number or null <float>

Overall satisfaction score if applicable

nps_score
integer or null [ 0 .. 10 ]

NPS score (0-10) if the survey contains an NPS question

provider
string
Enum: "liftify" "birdeye"

Survey provider

anonymous
boolean

Whether the response is anonymous

customer_display_name
string or null

Display name for the customer (e.g., "John D.")

(string or null) or (CustomerListItem (object or null))

The matched customer for this response. Null if anonymous/unmatched. Collapsed (default): The customer_id string (e.g., cus-<uuid>). Expanded (expand[]=customer): Full customer object with the same detail GET /customers/{id} returns, including email and custom_fields. The nested customer_campaigns and reviews fields are only included when nested-expanded (e.g. expand[]=customer.customer_campaigns).

string or LocationItem (object)

The location for this response. Collapsed (default): The location_id string. Expanded (expand[]=location): Full location object with the same detail GET /locations returns, including name and custom_fields.

string or SurveyItem (object)

The survey for this response. Collapsed (default): The survey public_id string. Expanded (expand[]=survey): Full survey object.

string or SurveyVersionItem (object)

The survey version for this response. Collapsed (default): The survey version public_id string. Expanded (expand[]=survey_version): Full survey version object. Use expand[]=survey_version.questions to expand questions within the version.

Array of strings or Array of SurveyAnswerItem (objects)

Answers to this survey response. Only present when expand options are used in the request. Collapsed (expand options present but answers not expanded): Array of answer public_id strings. Expanded (expand[]=answers): Array of full answer objects. Omitted when no expand options are present in the request.

object or null

The raw contact snapshot captured when the response was ingested or submitted, exactly as stored — contact details plus the original inbound contact payload (original_contact) with custom fields keyed by the labels they arrived with (e.g. a provider's job reference or branch identifier). Values reflect what was captured at survey time, independent of later edits to the customer record. Key structure varies by source: ingested responses carry the matched customer's details plus original_contact; Liftify public-form responses carry the contact-info answer fields only. Included (include[]=customer_snapshot): The stored snapshot object verbatim — the stored empty value ({} or null) for responses with no snapshot. Omitted when customer_snapshot is not requested via include[].

request_date
string or null <date-time>

When the survey was sent to the customer. Null when the send time is unknown.

response_date
string or null <date-time>

When the customer completed the survey. Null for responses the customer has not finished.

created_at
string <date-time>

When the response was created

updated_at
string <date-time>

When the response was last updated

Response samples

Content type
application/json
{
  • "public_id": "770e8400-e29b-41d4-a716-446655440002",
  • "completion_status": "complete",
  • "overall_score": 4.5,
  • "nps_score": 9,
  • "provider": "liftify",
  • "anonymous": false,
  • "customer_display_name": "John D.",
  • "request_date": "2025-08-14T09:00:00Z",
  • "response_date": "2025-08-16T08:50:18Z",
  • "created_at": "2025-08-15T16:37:20Z",
  • "updated_at": "2025-08-16T08:50:18Z"
}

Reviews

Access reviews and manage unsubscribes

Retrieve reviews for account

Retrieve all reviews for your entire account. Results are sorted by most recent review date.

Expandable fields: location, customer

When expanded, location includes the full location object with address, city, state, zipcode, country, phone number, timezone, and custom fields. When not expanded, location is a compact object with only location_id and name.

When expanded, customer includes the full customer object (nullable). When not expanded, customer_id is returned as a string (nullable) — the customer_id from the first confirmed match, or null if no confirmed match exists.

Authorizations:
BearerAuth
query Parameters
expand
Array of strings

Expand related objects inline instead of returning IDs. Pass one or more association names to include the full object in the response. Use dot-notation for nested expansion (e.g., expand[]=customer_campaigns.location).

Limits: Maximum 10 expand paths per request, maximum 3 levels of nesting depth.

Without expand: Related objects are either omitted or rendered as compact inline objects (e.g., { "location_id": "loc-...", "name": "..." }).

With expand: Related objects are rendered as full API objects with all their fields.

Error handling: Invalid expand paths return a 400 Bad Request with a message listing valid expansion options.

string or Array of strings

The location ID in the format of "loc-" followed by the UUID.

has_customer
boolean

Filter reviews that have or don't have a confirmed customer match. When true, returns only reviews with a confirmed customer match. When false, returns only reviews without a confirmed customer match.

string or Array of strings

Filter reviews matched to specific customer(s). Accepts a single customer ID or an array of customer IDs. Format is "cus-" followed by a UUID.

string or Array of strings

Filter by review provider. Accepts a single provider or an array of providers.

has_reply
boolean

Filter results to include only reviews with a reply

integer or Array of integers

Filter results to include only reviews matching any of the specified star ratings. Can be a single value or an array of values.

review_date_time_gt
string <date-time>
Examples:
  • review_date_time_gt=2025-08-15T14:30:00-04:00 - Full ISO 8601 with timezone offset (strictly greater than this instant)
  • review_date_time_gt=2025-08-15T18:30:00Z - UTC timestamp (Z)
  • review_date_time_gt=2025-08-15T18:30:00.123Z - Millisecond precision

Return reviews with a review_date_time strictly greater than this value. This is an ISO 8601 date-time string. Ignored when review_date_time_eq is present.

review_date_time_gte
string <date-time>
Examples:
  • review_date_time_gte=2025-08-15T00:00:00Z - Start of day inclusive (UTC)
  • review_date_time_gte=2025-08-15T09:15:00Z - Specific time inclusive threshold
  • review_date_time_gte=2025-10-31T00:00:00-05:00 - Edge case near boundary with timezone offset

Return reviews with a review_date_time greater than or equal to this value. This is an ISO 8601 date-time string. Ignored when review_date_time_eq is present.

review_date_time_lt
string <date-time>
Examples:
  • review_date_time_lt=2025-08-15T23:59:59Z - End of day exclusive threshold (use 23:59:59)
  • review_date_time_lt=2026-01-01T00:00:00Z - Exclude anything on/after this future time
  • review_date_time_lt=2025-08-16T00:00:00+02:00 - Timezone offset representation

Return reviews with a review_date_time strictly less than this value. This is an ISO 8601 date-time string. Ignored when review_date_time_eq is present.

review_date_time_lte
string <date-time>
Examples:
  • review_date_time_lte=2025-08-15T23:59:59Z - Inclusive end-of-period filtering
  • review_date_time_lte=2025-08-15T23:59:59Z - Use with review_date_time_gte to form a closed interval
  • review_date_time_lte=2025-08-15T23:59:59.999Z - Millisecond precision inclusive

Return reviews with a review_date_time less than or equal to this value. This is an ISO 8601 date-time string. Ignored when review_date_time_eq is present.

string or string
Examples:
  • review_date_time_eq=2025-08-15 - Date (YYYY-MM-DD)
  • review_date_time_eq=2020-02-29 - Leap year date (time ignored)
  • review_date_time_eq=2025-01-31 - End of month date (time ignored)
  • review_date_time_eq=2025-08-15T00:00:00Z - Date-time (time is ignored, date portion used)
  • review_date_time_eq=2025-08-15T22:00:00-04:00 - Date-time with timezone offset (time ignored)
  • review_date_time_eq=2025-08-15T10:30:00.789Z - Date-time with milliseconds (time ignored)

Filter reviews that occurred on the specified date, ignoring the time portion. Accepts either an ISO‑8601 date (YYYY‑MM‑DD) or a full ISO‑8601 date-time; only the date portion is used for filtering.

Precedence: When review_date_time_eq is provided, all other date-time filters (review_date_time_gt, review_date_time_gte, review_date_time_lt, review_date_time_lte) are ignored.

is_archived
boolean

Filters the result set by archive state.

  • true → returns only archived reviews
  • false → returns only active reviews
  • omitted → returns active reviews only (current default) When both is_archived and include_archived are provided, is_archived wins.
include_archived
boolean

When true, returns active AND archived reviews combined. When false or omitted, returns active reviews only. See is_archived for the explicit single-state selector; is_archived takes precedence when both are provided.

potential_violation
boolean

Filters by AI content-policy violation flag. true returns reviews carrying an active violation; false returns reviews without one. Only honored when the account has Google review violation detection enabled — ignored otherwise.

violation_status
Array of strings
Items Enum: "none" "flagged" "reported" "removed" "dismissed"

Filters by violation lifecycle status. Accepts one value or an array. Only honored when the account has violation detection enabled — ignored otherwise.

violation_category
Array of strings
Items Enum: "low_quality_information" "fake_or_deceptive" "profanity" "bullying_or_harassment" "discrimination_or_hate_speech" "personal_information"

Filters by AI-assigned violation category. Accepts one value or an array. Only honored when the account has violation detection enabled — ignored otherwise.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

Responses

Response Schema: application/json
Array of objects (Review)
Array
account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

review_id
string

Review ID starts with "rev-" then a UUID.

object or LocationItem (object)

The location associated with this review. Default: Compact object with location_id and name. Expanded (expand[]=location): Full location object matching the LocationItem schema.

customer_id
string or null

The matched customer for this review (collapsed form). Present when customer is not expanded. Returns the customer_id (e.g., cus-<uuid>) from the first confirmed customer match, or null if no confirmed match exists.

(CustomerListItem (object or null))

The matched customer for this review (expanded form). Present when expand[]=customer is requested. Default (no expand): The field is rendered as customer_id (string, nullable) instead. Expanded (expand[]=customer): Full customer object matching the CustomerListItem schema, or null if no confirmed match exists.

reviewer_display_name
string
reviewer_profile_photo_url
string <url>
star_rating
number <integer>

1-5 star rating

comment
string
reply_comment
string
reply_date_time
string or null <date-time>

ISO-8601 timestamp when the business response was posted; null when not replied.

review_date_time
string <date-time>
has_reply
boolean
provider
string
Enum: "google" "facebook" "yelp" "bbb" "homeadvisor"
archived_at
string or null <date-time>

ISO-8601 timestamp when the review was archived; null for active reviews.

first_missing_at
string or null <date-time>

ISO-8601 timestamp of the first time this review was detected as missing from the review platform. Set once and never cleared or overwritten — a review that is restored and later removed again keeps its original first-missing instant.

Read together with archived_at to classify a review's removal history from a single call, with no prior snapshot to diff against:

first_missing_at archived_at Meaning
present present Currently removed from the review platform
present null Previously removed, since restored
null null Never detected as missing

Returned on every review the caller is entitled to retrieve, regardless of the is_archived / include_archived filters.

Coverage limits. Removal detection currently runs for Google reviews only, and a location's removal history begins when detection was enabled for it — reviews removed before that point carry no marker.

potential_violation
boolean

AI content-policy violation flag (true for any active violation state). Only present when the account has Google review violation detection enabled.

violation_category
string or null
Enum: "low_quality_information" "fake_or_deceptive" "profanity" "bullying_or_harassment" "discrimination_or_hate_speech" "personal_information"

AI-assigned violation category; null when there is no violation. Only present when the account has violation detection enabled.

violation_reason
string or null

AI explanation of the violation; null when there is no violation. Only present when violation detection is enabled.

violation_status
string
Enum: "none" "flagged" "reported" "removed" "dismissed"

Violation lifecycle status. Only present when the account has violation detection enabled.

violation_reported_at
string or null <date-time>

ISO-8601 timestamp when the violation was reported to Google; null otherwise. Only present when violation detection is enabled.

created_at
string <date-time>
updated_at
string <date-time>

Response samples

Content type
application/json
{
  • "reviews": [
    • {
      • "account_id": "string",
      • "review_id": "string",
      • "location": {
        • "location_id": "string",
        • "name": "string"
        },
      • "customer_id": "string",
      • "customer": {
        • "customer_id": "string",
        • "account_id": "string",
        • "locations": [
          • {
            }
          ],
        • "first_name": "string",
        • "last_name": "string",
        • "email": "[email protected]",
        • "phone_number": "+15555555555",
        • "lead_source": "google",
        • "normalized_lead_source": "google",
        • "lead_source_channel_type": "Google",
        • "status": "active",
        • "text_opt_in": true,
        • "created_at": "2019-08-24T14:15:22Z",
        • "updated_at": "2019-08-24T14:15:22Z",
        • "custom_fields": [
          • {
            }
          ],
        • "tags": [
          • "VIP",
          • "Dissatisfied"
          ],
        • "last_activity_at": "2019-08-24T14:15:22Z",
        • "customer_campaigns": [
          • 0
          ],
        • "reviews": [
          • "string"
          ]
        },
      • "reviewer_display_name": "string",
      • "reviewer_profile_photo_url": "string",
      • "star_rating": 0,
      • "comment": "string",
      • "reply_comment": "string",
      • "reply_date_time": "2019-08-24T14:15:22Z",
      • "review_date_time": "2019-08-24T14:15:22Z",
      • "has_reply": true,
      • "provider": "google",
      • "archived_at": "2019-08-24T14:15:22Z",
      • "first_missing_at": "2019-08-24T14:15:22Z",
      • "potential_violation": true,
      • "violation_category": "low_quality_information",
      • "violation_reason": "string",
      • "violation_status": "none",
      • "violation_reported_at": "2019-08-24T14:15:22Z",
      • "created_at": "2019-08-24T14:15:22Z",
      • "updated_at": "2019-08-24T14:15:22Z"
      }
    ]
}

Retrieve unsubscribes for account

Retrieve all unsubscribed customers for your entire account. Returns both fully unsubscribed customers and partially unsubscribed customers (with category-specific unsubscribe preferences). Results are sorted by most recent unsubscribe date.

Authorizations:
BearerAuth
path Parameters
account_id
required
string

The account ID in the format of "acc-" followed by the UUID.

query Parameters
page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

start_date
string <date-time>

Filter results to include only unsubscribes from this date onwards

end_date
string <date-time>

Filter results to include only unsubscribes up to this date

Responses

Response Schema: application/json
total_count
integer <int32>
total_pages
integer <int32>
current_page
integer <int8>
Array of objects (LocationUnSubscribeItem)

Response samples

Content type
application/json
{
  • "total_count": 2,
  • "total_pages": 1,
  • "current_page": 1,
  • "customers": [
    • {
      • "first_name": "Jane",
      • "last_name": "Smith",
      • "phone": "+15551234568",
      • "email": "[email protected]",
      • "created_at": "2025-08-15T16:37:20Z",
      • "unsubscribe_type": "partial",
      • "unsubscribed_date": "2025-08-16T08:50:18Z",
      • "unsubscribe_categories": [
        • "marketing"
        ],
      • "account": {
        • "name": "Test Company",
        • "id": "acc-test-402efb14-f779-4dfe-9a07-feb72602c8bb"
        },
      • "location": {
        • "name": "Test Location",
        • "id": "loc-test-8c0a8daa-dbd8-4e7c-bd38-d810795512e3"
        },
      • "custom_fields": { }
      },
    • {
      • "first_name": "John",
      • "last_name": "Doe",
      • "phone": "+15551234567",
      • "email": "[email protected]",
      • "created_at": "2025-08-15T16:37:20Z",
      • "unsubscribe_type": "full",
      • "unsubscribed_date": "2025-08-15T16:37:20Z",
      • "unsubscribe_categories": [
        • "feedback_informational",
        • "marketing",
        • "transactional"
        ],
      • "account": {
        • "name": "Test Company",
        • "id": "acc-test-402efb14-f779-4dfe-9a07-feb72602c8bb"
        },
      • "location": {
        • "name": "Test Location",
        • "id": "loc-test-8c0a8daa-dbd8-4e7c-bd38-d810795512e3"
        },
      • "custom_fields": { }
      }
    ]
}

Retrieve unsubscribes for location

Retrieve all unsubscribed customers for a specific location. Returns both fully unsubscribed customers and partially unsubscribed customers (with category-specific unsubscribe preferences). Results are sorted by most recent unsubscribe date.

Authorizations:
BearerAuth
path Parameters
location_id
required
string

The location ID in the format of "loc-" followed by the UUID.

query Parameters
page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

start_date
string <date-time>

Filter results to include only unsubscribes from this date onwards

end_date
string <date-time>

Filter results to include only unsubscribes up to this date

Responses

Response Schema: application/json
total_count
integer <int32>
total_pages
integer <int32>
current_page
integer <int8>
Array of objects (LocationUnSubscribeItem)

Response samples

Content type
application/json
{
  • "total_count": 2,
  • "total_pages": 1,
  • "current_page": 1,
  • "customers": [
    • {
      • "first_name": "Jane",
      • "last_name": "Smith",
      • "phone": "+15551234568",
      • "email": "[email protected]",
      • "created_at": "2025-08-15T16:37:20Z",
      • "unsubscribe_type": "partial",
      • "unsubscribed_date": "2025-08-16T08:50:18Z",
      • "unsubscribe_categories": [
        • "marketing"
        ],
      • "account": {
        • "name": "Test Company",
        • "id": "acc-test-402efb14-f779-4dfe-9a07-feb72602c8bb"
        },
      • "location": {
        • "name": "Test Location",
        • "id": "loc-test-8c0a8daa-dbd8-4e7c-bd38-d810795512e3"
        },
      • "custom_fields": { }
      },
    • {
      • "first_name": "John",
      • "last_name": "Doe",
      • "phone": "+15551234567",
      • "email": "[email protected]",
      • "created_at": "2025-08-15T16:37:20Z",
      • "unsubscribe_type": "full",
      • "unsubscribed_date": "2025-08-15T16:37:20Z",
      • "unsubscribe_categories": [
        • "feedback_informational",
        • "marketing",
        • "transactional"
        ],
      • "account": {
        • "name": "Test Company",
        • "id": "acc-test-402efb14-f779-4dfe-9a07-feb72602c8bb"
        },
      • "location": {
        • "name": "Test Location",
        • "id": "loc-test-8c0a8daa-dbd8-4e7c-bd38-d810795512e3"
        },
      • "custom_fields": { }
      }
    ]
}

Tickets

Access support tickets

Retrieve tickets

Retrieve a paginated list of tickets for the authenticated account with optional filtering.

Expandable fields: customer, location

When expanded, customer includes the full customer object with all fields. When not expanded, customer is a compact object with full_name, email, and phone_number.

When expanded, location includes the full location object with address, city, state, zipcode, country, phone number, timezone, and custom fields. When not expanded, location is a compact object with only name.

Authorizations:
BearerAuth
query Parameters
string or Array of strings

The location ID in the format of "loc-" followed by the UUID.

expand
Array of strings

Expand related objects inline instead of returning IDs. Pass one or more association names to include the full object in the response. Use dot-notation for nested expansion (e.g., expand[]=customer_campaigns.location).

Limits: Maximum 10 expand paths per request, maximum 3 levels of nesting depth.

Without expand: Related objects are either omitted or rendered as compact inline objects (e.g., { "location_id": "loc-...", "name": "..." }).

With expand: Related objects are rendered as full API objects with all their fields.

Error handling: Invalid expand paths return a 400 Bad Request with a message listing valid expansion options.

status
string

Filter tickets by status name.

status_type
string

Filter tickets by status type.

assigned_to
string

Filter tickets by assigned user ID. Use "unassigned" to find tickets without an assignee.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

created_at_gt
string <date-time>

Return tickets with a created_at strictly greater than this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

created_at_gte
string <date-time>

Return tickets with a created_at greater than or equal to this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

created_at_lt
string <date-time>

Return tickets with a created_at strictly less than this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

created_at_lte
string <date-time>

Return tickets with a created_at less than or equal to this value. This is an ISO 8601 date-time string. Ignored when created_at_eq is present.

string or string

Filter tickets created on the specified date, ignoring the time portion.

Precedence: When created_at_eq is provided, all other date-time filters are ignored.

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of tickets matching the filter criteria

total_pages
integer <int32>

Total number of pages available

current_page
integer <int8>

Current page number

Array of objects (TicketItem)

Response samples

Content type
application/json
{
  • "total_count": 0,
  • "total_pages": 0,
  • "current_page": 0,
  • "tickets": [
    • {
      • "id": 0,
      • "ticket_id": "string",
      • "name": "string",
      • "description": "string",
      • "status": {
        • "name": "string",
        • "color": "string",
        • "status_type": "string"
        },
      • "location": {
        • "name": "string"
        },
      • "customer": {},
      • "assigned_to": {},
      • "source_type": "review",
      • "source_id": "string",
      • "created_at": "2019-08-24T14:15:22Z",
      • "updated_at": "2019-08-24T14:15:22Z",
      • "closed_at": "2019-08-24T14:15:22Z"
      }
    ]
}

Retrieve a single ticket

Retrieve a single ticket by its ticket ID. The ticket must belong to your account.

Expandable fields: customer, location

Authorizations:
BearerAuth
path Parameters
ticket_id
required
string

The ticket ID.

query Parameters
expand
Array of strings

Expand related objects inline instead of returning IDs. Pass one or more association names to include the full object in the response. Use dot-notation for nested expansion (e.g., expand[]=customer_campaigns.location).

Limits: Maximum 10 expand paths per request, maximum 3 levels of nesting depth.

Without expand: Related objects are either omitted or rendered as compact inline objects (e.g., { "location_id": "loc-...", "name": "..." }).

With expand: Related objects are rendered as full API objects with all their fields.

Error handling: Invalid expand paths return a 400 Bad Request with a message listing valid expansion options.

Responses

Response Schema: application/json
id
integer <int64>

Internal ticket ID

ticket_id
string

Public ticket ID

name
string

Ticket name/title

description
string or null

Ticket description

object or null

Current ticket status (always rendered as inline object)

object or LocationItem (object)

The location associated with this ticket. Default: Compact object with name. Expanded (expand[]=location): Full location object with address, city, state, etc.

(object or null) or (CustomerListItem (object or null))

The customer associated with this ticket. Default: Compact object with full_name, email, and phone_number. Expanded (expand[]=customer): Full customer object.

object or null

Assigned user (always rendered as inline object)

source_type
string
Enum: "review" "survey" "manual"

Source of the ticket

source_id
string or null

ID of the source object (review or survey response)

created_at
string <date-time>

When the ticket was created

updated_at
string <date-time>

When the ticket was last updated

closed_at
string or null <date-time>

When the ticket entered a done (closed) status

Response samples

Content type
application/json
{
  • "id": 0,
  • "ticket_id": "string",
  • "name": "string",
  • "description": "string",
  • "status": {
    • "name": "string",
    • "color": "string",
    • "status_type": "string"
    },
  • "location": {
    • "name": "string"
    },
  • "customer": {},
  • "assigned_to": {},
  • "source_type": "review",
  • "source_id": "string",
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z",
  • "closed_at": "2019-08-24T14:15:22Z"
}

Locations

Access account locations

Retrieve locations

Retrieve a paginated list of all locations for the authenticated account. Since API keys are account-scoped, all account locations are accessible. Use the optional location_ids filter to retrieve a specific subset.

Authorizations:
BearerAuth
query Parameters
string or Array of strings

Filter results to include only the specified location IDs. Accepts a single location ID or an array of location IDs.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of locations matching the filter criteria

total_pages
integer <int32>

Total number of pages available

current_page
integer <int8>

Current page number

Array of objects (LocationItem)

Response samples

Content type
application/json
{
  • "total_count": 3,
  • "total_pages": 1,
  • "current_page": 1,
  • "locations": [
    • {
      • "location_id": "loc-test-8c0a8daa-dbd8-4e7c-bd38-d810795512e3",
      • "name": "Downtown Office",
      • "address": "123 Main St",
      • "city": "Springfield",
      • "state": "IL",
      • "zipcode": "62701",
      • "country": "US",
      • "phone_number": "+15551234567",
      • "timezone": "America/Chicago",
      • "custom_fields": [
        • {
          • "name": "brand",
          • "label": "Brand",
          • "value": "Acme Corp",
          • "type": "text"
          }
        ]
      }
    ]
}

Campaigns

Access campaign templates

Retrieve active campaigns

Retrieve a paginated list of all active, non-archived campaign templates for the authenticated account. A campaign is considered active when it is assigned to at least one location with an active, non-archived configuration.

Authorizations:
BearerAuth
query Parameters
string or Array of strings

Filter results to include only campaigns assigned to the specified locations. Accepts a single location ID or an array of location IDs.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of active campaigns matching the filter criteria

total_pages
integer <int32>

Total number of pages available

current_page
integer <int8>

Current page number

Array of objects (CampaignTemplateItem)

Response samples

Content type
application/json
{
  • "total_count": 2,
  • "total_pages": 1,
  • "current_page": 1,
  • "campaigns": [
    • {
      • "campaign_template_id": "550e8400-e29b-41d4-a716-446655440000",
      • "name": "Post-Service Review Request",
      • "description": "Automated review request sent after service completion",
      • "campaign_type": "reviews",
      • "campaign_category": "feedback_informational",
      • "send_immediately": false,
      • "block_if_in_other_campaign": true,
      • "created_at": "2025-08-15T16:37:20Z",
      • "updated_at": "2025-08-16T08:50:18Z"
      }
    ]
}

External Systems

Discover the catalog of third-party systems integrated with your account

List external systems

Retrieve a paginated list of external systems configured for the authenticated account.

An external system represents a third-party integration (e.g. ServiceTitan, HubSpot) that maps Liftify records — customers, tickets, locations, etc. — to identifiers in the external system.

Authorizations:
BearerAuth
query Parameters
status
string
Enum: "active" "inactive"

Filter by active state. Accepts active or inactive. Any other value (including absent) returns all systems.

search
string

Substring search against display_name and slug (case-insensitive). LIKE wildcards (%, _) in the input are escaped and matched literally.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of external systems matching the filter criteria

total_pages
integer <int32>

Total number of pages available

current_page
integer <int8>

Current page number

Array of objects (ExternalSystemItem)

Response samples

Content type
application/json
{
  • "total_count": 1,
  • "total_pages": 1,
  • "current_page": 1,
  • "external_systems": [
    • {
      • "id": 1,
      • "slug": "servicetitan",
      • "display_name": "ServiceTitan",
      • "description": "ServiceTitan integration for job and customer sync",
      • "active": true,
      • "created_at": "2026-04-15T16:37:20Z",
      • "updated_at": "2026-04-15T16:37:20Z"
      }
    ]
}

Retrieve a single external system

Retrieve a single external system by its slug. The system must belong to your account; matching slugs in other accounts return 404.

Authorizations:
BearerAuth
path Parameters
slug
required
string

The external system's slug (e.g. servicetitan). Slugs may contain dots, hyphens, and underscores.

Responses

Response Schema: application/json
id
integer

Internal numeric identifier

slug
string

Public, immutable identifier used as the external system's stable handle (e.g. servicetitan, hubspot)

display_name
string

Human-readable name of the integration

description
string or null

Optional free-text description of the integration

active
boolean

Whether the system is currently enabled for the account

created_at
string <date-time>

When the external system entry was created (ISO 8601)

updated_at
string <date-time>

When the external system entry was last updated (ISO 8601)

Response samples

Content type
application/json
{
  • "id": 0,
  • "slug": "string",
  • "display_name": "string",
  • "description": "string",
  • "active": true,
  • "created_at": "2019-08-24T14:15:22Z",
  • "updated_at": "2019-08-24T14:15:22Z"
}

External Aliases

Look up mappings between Liftify records and their identifiers in external systems

List external aliases

Retrieve a paginated list of external alias mappings for the authenticated account.

Filter combinations express both inbound and outbound resolution:

  • Inbound (a webhook arrived with an external id — what Liftify record is that?): combine external_system_slug, external_entity_type, and external_id.
  • Outbound (for this Liftify record, what is its external id?): combine aliasable_type and aliasable_id.

external_entity_type is normalized to lowercase server-side (Job and job both match a stored value of job). aliasable_type is the literal Ruby class name (Ticket, Customer, Location, Review, Conversation, User) and is not normalized — ?aliasable_type=ticket returns nothing if the stored value is Ticket.

Authorizations:
BearerAuth
query Parameters
external_system_slug
string

Filter by the slug of the related external system.

external_entity_type
string

Filter by the kind of external record (e.g. customer, job, invoice). Normalized to lowercase server-side.

external_id
string

Filter by the identifier in the external system. Coerced to string before comparison.

external_ids
string
Example: external_ids=54607865,54688362

Bulk filter — return only aliases whose external_id is in this list. Accepts a comma-separated string or repeated array params. Maximum 500 ids per request (400 above the cap). Takes precedence over external_id when both are supplied; a supplied-but-empty list matches nothing.

aliasable_type
string
Enum: "Customer" "Location" "Review" "Ticket" "Conversation" "User"

Filter by the Liftify record's polymorphic class name. Case-sensitive — must match the stored value exactly (e.g. Ticket, Customer).

aliasable_id
integer

Filter by the Liftify record's primary key.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of external aliases matching the filter criteria

total_pages
integer <int32>

Total number of pages available

current_page
integer <int8>

Current page number

Array of objects (ExternalAliasItem)

Response samples

Content type
application/json
{
  • "total_count": 1,
  • "total_pages": 1,
  • "current_page": 1,
  • "external_aliases": [
    • {
      • "id": 42,
      • "external_system_slug": "servicetitan",
      • "external_entity_type": "job",
      • "external_id": "12345",
      • "created_at": "2026-04-15T16:37:20Z"
      }
    ]
}

Sites

Manage service-address Sites (list, retrieve, create, update) — writes flow through a dedup pipeline that honors external-system IDs and normalized addresses

List sites

Retrieve a paginated list of Sites for the authenticated account.

Filter combinations:

  • By customer — pass customer_id (the public cus- identifier) to restrict results to sites currently linked to that customer.
  • By external identifier — pass external_system (slug) together with external_id to look up the Site that maps to that external record. Optionally narrow further with external_entity_type (defaults to site).
  • By address substring — pass address_substring to match against address_line1, address_line2, or the deterministic normalized_address. Case-insensitive (ILIKE).

Archived sites are excluded by default.

Authorizations:
BearerAuth
query Parameters
customer_id
string

Filter to sites currently linked to the specified Customer (public cus-... ID). When this filter is present, each returned site includes a primary boolean indicating whether it is that customer's primary site.

external_system
string

Slug of the external system (e.g. servicetitan). Combine with external_id.

external_id
string

External identifier to look up. Combine with external_system.

external_entity_type
string
Default: "site"

External entity type (defaults to site when external_system/external_id are supplied). Normalized to lowercase.

address_substring
string

Case-insensitive substring matched against address_line1, address_line2, and normalized_address.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of sites matching the filter criteria

total_pages
integer <int32>

Total number of pages available

current_page
integer <int32>

Current page number

Array of objects (SiteListItem)

Response samples

Content type
application/json
{
  • "total_count": 1,
  • "total_pages": 1,
  • "current_page": 1,
  • "sites": [
    • {
      • "id": 42,
      • "name": "Smith Residence",
      • "address_line1": "123 Main St",
      • "city": "Seattle",
      • "region": "WA",
      • "postal_code": "98101",
      • "country": "US"
      }
    ]
}

Create or dedupe a Site

Create a Site, routed through a dedup pipeline:

  1. If external_id.external_system + external_id.external_id are supplied and already map to a Site in this account, that Site is returned (200 OK, no new alias row created).
  2. Otherwise, if the normalized form of the address (case + punctuation + whitespace) already matches an existing active Site in this account, that Site is returned (200 OK). This step only runs when address_line1, city, region, and postal_code are all present.
  3. Otherwise a new Site is inserted; when external_id is supplied an ExternalAlias row is registered in the same transaction (201 Created).

Sites are account-isolated: identical addresses in two different accounts produce two distinct Sites. Address fields are optional — a Site may be created with a partial address or none at all — but the request must carry some identity: a site object whose attributes are all blank, with no external_id, is rejected with 422.

Authorizations:
BearerAuth
Request Body schema: application/json
required
object
object or null

Optional external-system reference. Used as the primary dedup key on create and as an alias-upsert on update.

Responses

Response Schema: application/json
object (Site)

Full representation of a Site used in show/create/update responses.

id
integer <int64>

Internal numeric identifier of the Site

name
string or null
address_line1
string or null
address_line2
string or null
city
string or null
region
string or null
postal_code
string or null
country
string or null

ISO-3166-1 alpha-2 country code

access_notes
string or null
normalized_address
string

Deterministic dedup key — case + punctuation + whitespace normalized form of the six address fields, joined with " | ".

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

archived_at
string or null <date-time>

When the Site was soft-deleted (ISO 8601). Null when active.

created_at
string <date-time>
updated_at
string <date-time>
Array of objects (SiteExternalAlias)

Every external alias registered against this Site, across all of the account's active external systems.

Response Schema: application/json
object (Site)

Full representation of a Site used in show/create/update responses.

id
integer <int64>

Internal numeric identifier of the Site

name
string or null
address_line1
string or null
address_line2
string or null
city
string or null
region
string or null
postal_code
string or null
country
string or null

ISO-3166-1 alpha-2 country code

access_notes
string or null
normalized_address
string

Deterministic dedup key — case + punctuation + whitespace normalized form of the six address fields, joined with " | ".

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

archived_at
string or null <date-time>

When the Site was soft-deleted (ISO 8601). Null when active.

created_at
string <date-time>
updated_at
string <date-time>
Array of objects (SiteExternalAlias)

Every external alias registered against this Site, across all of the account's active external systems.

Request samples

Content type
application/json
{
  • "site": {
    • "name": "string",
    • "address_line1": "string",
    • "address_line2": "string",
    • "city": "string",
    • "region": "string",
    • "postal_code": "string",
    • "country": "US",
    • "access_notes": "string"
    },
  • "external_id": {
    • "external_system": "string",
    • "external_entity_type": "site",
    • "external_id": "string"
    }
}

Response samples

Content type
application/json
{
  • "site": {
    • "id": 0,
    • "name": "string",
    • "address_line1": "string",
    • "address_line2": "string",
    • "city": "string",
    • "region": "string",
    • "postal_code": "string",
    • "country": "string",
    • "access_notes": "string",
    • "normalized_address": "string",
    • "account_id": "string",
    • "archived_at": "2019-08-24T14:15:22Z",
    • "created_at": "2019-08-24T14:15:22Z",
    • "updated_at": "2019-08-24T14:15:22Z",
    • "external_aliases": [
      • {
        • "external_system_slug": "string",
        • "external_entity_type": "string",
        • "external_id": "string"
        }
      ]
    }
}

Retrieve a single Site

Retrieve a Site by its integer primary key. The Site must belong to your account; cross-account IDs return 404.

Authorizations:
BearerAuth
path Parameters
id
required
integer <int64>

Integer primary key of the Site

Responses

Response Schema: application/json
object (Site)

Full representation of a Site used in show/create/update responses.

id
integer <int64>

Internal numeric identifier of the Site

name
string or null
address_line1
string or null
address_line2
string or null
city
string or null
region
string or null
postal_code
string or null
country
string or null

ISO-3166-1 alpha-2 country code

access_notes
string or null
normalized_address
string

Deterministic dedup key — case + punctuation + whitespace normalized form of the six address fields, joined with " | ".

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

archived_at
string or null <date-time>

When the Site was soft-deleted (ISO 8601). Null when active.

created_at
string <date-time>
updated_at
string <date-time>
Array of objects (SiteExternalAlias)

Every external alias registered against this Site, across all of the account's active external systems.

Response samples

Content type
application/json
{
  • "site": {
    • "id": 0,
    • "name": "string",
    • "address_line1": "string",
    • "address_line2": "string",
    • "city": "string",
    • "region": "string",
    • "postal_code": "string",
    • "country": "string",
    • "access_notes": "string",
    • "normalized_address": "string",
    • "account_id": "string",
    • "archived_at": "2019-08-24T14:15:22Z",
    • "created_at": "2019-08-24T14:15:22Z",
    • "updated_at": "2019-08-24T14:15:22Z",
    • "external_aliases": [
      • {
        • "external_system_slug": "string",
        • "external_entity_type": "string",
        • "external_id": "string"
        }
      ]
    }
}

Update a Site

Update mutable Site attributes. Idempotent: applying the same payload twice yields the same final state.

An optional external_id object can be supplied to register a new ExternalAlias against the Site; the operation is itself idempotent on the alias tuple (re-posting the same external_system + external_id does not create a duplicate alias row).

Authorizations:
BearerAuth
path Parameters
id
required
integer <int64>

Integer primary key of the Site

Request Body schema: application/json
required
object
object or null

Optional external-system reference. Used as the primary dedup key on create and as an alias-upsert on update.

Responses

Response Schema: application/json
object (Site)

Full representation of a Site used in show/create/update responses.

id
integer <int64>

Internal numeric identifier of the Site

name
string or null
address_line1
string or null
address_line2
string or null
city
string or null
region
string or null
postal_code
string or null
country
string or null

ISO-3166-1 alpha-2 country code

access_notes
string or null
normalized_address
string

Deterministic dedup key — case + punctuation + whitespace normalized form of the six address fields, joined with " | ".

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

archived_at
string or null <date-time>

When the Site was soft-deleted (ISO 8601). Null when active.

created_at
string <date-time>
updated_at
string <date-time>
Array of objects (SiteExternalAlias)

Every external alias registered against this Site, across all of the account's active external systems.

Request samples

Content type
application/json
{
  • "site": {
    • "name": "string",
    • "address_line1": "string",
    • "address_line2": "string",
    • "city": "string",
    • "region": "string",
    • "postal_code": "string",
    • "country": "US",
    • "access_notes": "string"
    },
  • "external_id": {
    • "external_system": "string",
    • "external_entity_type": "site",
    • "external_id": "string"
    }
}

Response samples

Content type
application/json
{
  • "site": {
    • "id": 0,
    • "name": "string",
    • "address_line1": "string",
    • "address_line2": "string",
    • "city": "string",
    • "region": "string",
    • "postal_code": "string",
    • "country": "string",
    • "access_notes": "string",
    • "normalized_address": "string",
    • "account_id": "string",
    • "archived_at": "2019-08-24T14:15:22Z",
    • "created_at": "2019-08-24T14:15:22Z",
    • "updated_at": "2019-08-24T14:15:22Z",
    • "external_aliases": [
      • {
        • "external_system_slug": "string",
        • "external_entity_type": "string",
        • "external_id": "string"
        }
      ]
    }
}

Queue a Site CSV export

Queue an asynchronous CSV snapshot of every active Site in the authenticated account. Returns 202 Accepted with an export object whose token is the public handle for the status-poll and download endpoints. Requires an account API key — the read-only all-access pass cannot queue exports. Exports are capped at a per-account row limit; when the cap drops matching records the status response sets truncated to true.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "export": {
    • "token": "0123456789abcdef0123456789abcdef",
    • "report_type": "site_csv",
    • "status": "pending",
    • "record_count": 0,
    • "truncated": true,
    • "error_message": "string",
    • "expires_at": "2019-08-24T14:15:22Z",
    • "download_url": "string"
    }
}

Poll a Site CSV export

Retrieve the status of a Site CSV export by token. When the export is completed and unexpired, the payload includes a short-lived signed download_url.

Authorizations:
BearerAuth
path Parameters
token
required
string

Export token returned by the queue endpoint — the public handle for one CSV export run.

Responses

Response Schema: application/json
object (CsvExport)

One entity CSV export run. token is the public handle for the status poll and download endpoints. download_url is present only on status responses for a completed, unexpired export; the URL is a short-lived signed link to the CSV file.

token
string
report_type
string
Enum: "site_csv" "job_csv" "employee_csv"
status
string
Enum: "pending" "processing" "completed" "failed" "canceled"
record_count
integer or null

Rows in the generated CSV (set on completion).

truncated
boolean

True when the per-account row cap dropped matching records, so the CSV is a partial snapshot.

error_message
string or null

Generic failure reason. Internal exception detail is deliberately withheld and kept in Liftify's logs.

expires_at
string <date-time>

When the export and its download links expire (7 days after the request).

download_url
string or null

Short-lived signed download URL; only present on status responses when the export is completed and unexpired.

Response samples

Content type
application/json
{
  • "export": {
    • "token": "0123456789abcdef0123456789abcdef",
    • "report_type": "site_csv",
    • "status": "pending",
    • "record_count": 0,
    • "truncated": true,
    • "error_message": "string",
    • "expires_at": "2019-08-24T14:15:22Z",
    • "download_url": "string"
    }
}

Download a completed Site CSV export

Redirects to a time-limited signed object-storage URL for the completed export file.

Authorizations:
BearerAuth
path Parameters
token
required
string

Export token returned by the queue endpoint — the public handle for one CSV export run.

Responses

Response samples

Content type
application/json
{
  • "status": "success",
  • "message": "string",
  • "code": 0,
  • "error": "string"
}

Jobs

Manage Jobs (list, retrieve, create, update) — writes route through an alias-only resolver; without an external_id, every POST creates a new Job

List jobs

Retrieve a paginated list of Jobs for the authenticated account. All responses respect tenant isolation — Jobs are scoped to the bearer token's account.

Filter combinations:

  • By customer — pass customer (the public cus- identifier) to restrict to Jobs whose customer_id resolves to that Customer in this account.
  • By site — pass site (Site's integer primary key; Site has no string public_id) to restrict to Jobs whose site_id resolves to that Site in this account.
  • By location — pass location (the public loc- identifier) to restrict to Jobs at that Location.
  • By status — pass status to match against Job#job_status literally.
  • By job-date range — pass job_date_from and/or job_date_to as ISO 8601 dates (YYYY-MM-DD). Both bounds are inclusive.
  • By external identifier — pass external_system (slug) together with external_id to look up Jobs that map to that external record. Optionally narrow further with external_entity_type (defaults to job). Unlike Sites listing, when no alias resolves to a Job in this account the response is 404 Not Found rather than an empty list.

Cross-account ids supplied for customer, site, or location return an empty result set, never records owned by another account.

Authorizations:
BearerAuth
query Parameters
customer
string

Filter to Jobs whose Customer matches the supplied public cus-... identifier.

site
integer <int64>

Filter to Jobs whose Site matches the supplied integer primary key. Site has no string public_id — its integer id IS its stable external identifier.

location
string

Filter to Jobs at the Location matching the supplied public loc-... identifier.

status
string

Filter to Jobs whose job_status matches the supplied value (literal string match).

job_date_from
string <date>

Inclusive lower bound on job_date (ISO 8601 YYYY-MM-DD).

job_date_to
string <date>

Inclusive upper bound on job_date (ISO 8601 YYYY-MM-DD).

external_system
string

Slug of the external system (e.g. servicetitan). Combine with external_id.

external_id
string

External identifier to look up. Combine with external_system.

external_entity_type
string
Default: "job"

External entity type (defaults to job when external_system/external_id are supplied). Normalized to lowercase.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of jobs matching the filter criteria

total_pages
integer <int32>

Total number of pages available

current_page
integer <int32>

Current page number

Array of objects (JobListItem)

Response samples

Content type
application/json
{
  • "total_count": 1,
  • "total_pages": 1,
  • "current_page": 1,
  • "jobs": [
    • {
      • "id": 87,
      • "public_id": "job-2c1bd9e6-7c52-4f08-9b86-2c0e3a8a7c11",
      • "name": "Spring HVAC tune-up",
      • "job_status": "completed",
      • "job_date": "2026-05-12",
      • "customer_id": "cus-1d0e6e2b-8b1f-44a8-9c39-12f7c1a93b22",
      • "location_id": "loc-9c2c4f7e-c1ba-4d2e-9a18-66f0c9c1d101",
      • "site_id": 42,
      • "invoice_amount": 489.5,
      • "currency": "USD"
      }
    ]
}

Create or dedupe a Job

Create a Job, routed through the JobResolver pipeline:

  1. If external_id.external_system + external_id.external_id are supplied and already map to a Job in this account, that Job is returned (200 OK, no new alias row created).
  2. Otherwise a new Job is inserted; when external_id is supplied an ExternalAlias row is registered in the same transaction (201 Created).

Unlike Sites, Jobs have no natural-key dedup. Without an external_id payload, every POST creates a new Job — even if a Job with identical attributes already exists.

Jobs are account-isolated: identical external_id tuples across two different accounts produce two distinct Jobs.

Authorizations:
BearerAuth
Request Body schema: application/json
required
object
object or null

Optional external-system reference. Used as the primary dedup key on create and as an alias-upsert on update. Both external_system and external_id must be supplied together — providing one without the other returns 422.

Responses

Response Schema: application/json
object (Job)

Full representation of a Job used in show/create/update responses (rendered from JobBlueprint :normal).

id
integer <int64>

Internal numeric identifier of the Job

public_id
string

Stable external identifier in the format job-{uuid}

name
string or null
job_status
string or null
job_date
string or null <date>
customer_id
string or null

Customer ID starts with "cus-" then a UUID. Required if account_id is blank.

location_id
string or null

Location ID starts with "loc-" then a UUID. Required if account_id is blank.

site_id
integer or null <int64>

Site's integer primary key.

invoice_amount
number or null <decimal>
currency
string = 3 characters
Default: "USD"

ISO 4217 currency code; defaults to USD.

source
string or null

Free-form provenance label (e.g. servicetitan, manual).

job_type
string or null
business_unit
string or null
description
string or null
scheduled_date
string or null <date>
completed_date
string or null <date>
review_request_opt_out
boolean
Default: false

When true, the job is excluded from review-request campaigns.

visit_count
integer
Default: 0

Number of distinct appointments/visits recorded for this job. Read-only; populated from the source system during ingestion.

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

created_at
string <date-time>
updated_at
string <date-time>
Array of objects (ActiveFieldValue)

Rendered custom-field values from the active_values association. Empty values are filtered out.

Array of objects (JobExternalAlias)

Every external alias registered against this Job, across all of the account's active external systems.

Response Schema: application/json
object (Job)

Full representation of a Job used in show/create/update responses (rendered from JobBlueprint :normal).

id
integer <int64>

Internal numeric identifier of the Job

public_id
string

Stable external identifier in the format job-{uuid}

name
string or null
job_status
string or null
job_date
string or null <date>
customer_id
string or null

Customer ID starts with "cus-" then a UUID. Required if account_id is blank.

location_id
string or null

Location ID starts with "loc-" then a UUID. Required if account_id is blank.

site_id
integer or null <int64>

Site's integer primary key.

invoice_amount
number or null <decimal>
currency
string = 3 characters
Default: "USD"

ISO 4217 currency code; defaults to USD.

source
string or null

Free-form provenance label (e.g. servicetitan, manual).

job_type
string or null
business_unit
string or null
description
string or null
scheduled_date
string or null <date>
completed_date
string or null <date>
review_request_opt_out
boolean
Default: false

When true, the job is excluded from review-request campaigns.

visit_count
integer
Default: 0

Number of distinct appointments/visits recorded for this job. Read-only; populated from the source system during ingestion.

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

created_at
string <date-time>
updated_at
string <date-time>
Array of objects (ActiveFieldValue)

Rendered custom-field values from the active_values association. Empty values are filtered out.

Array of objects (JobExternalAlias)

Every external alias registered against this Job, across all of the account's active external systems.

Request samples

Content type
application/json
{
  • "job": {
    • "customer_id": 0,
    • "location_id": 0,
    • "site_id": 0,
    • "source": "string",
    • "name": "string",
    • "job_type": "string",
    • "job_status": "string",
    • "business_unit": "string",
    • "description": "string",
    • "invoice_amount": 0,
    • "currency": "USD",
    • "scheduled_date": "2019-08-24",
    • "completed_date": "2019-08-24",
    • "job_date": "2019-08-24",
    • "review_request_opt_out": false
    },
  • "external_id": {
    • "external_system": "string",
    • "external_entity_type": "job",
    • "external_id": "string"
    }
}

Response samples

Content type
application/json
{
  • "job": {
    • "id": 0,
    • "public_id": "string",
    • "name": "string",
    • "job_status": "string",
    • "job_date": "2019-08-24",
    • "customer_id": "string",
    • "location_id": "string",
    • "site_id": 0,
    • "invoice_amount": 0,
    • "currency": "USD",
    • "source": "string",
    • "job_type": "string",
    • "business_unit": "string",
    • "description": "string",
    • "scheduled_date": "2019-08-24",
    • "completed_date": "2019-08-24",
    • "review_request_opt_out": false,
    • "visit_count": 0,
    • "account_id": "string",
    • "created_at": "2019-08-24T14:15:22Z",
    • "updated_at": "2019-08-24T14:15:22Z",
    • "custom_attributes": [
      • {
        • "name": "string",
        • "label": "string",
        • "value": "string",
        • "type": "string"
        }
      ],
    • "external_aliases": [
      • {
        • "external_system_slug": "string",
        • "external_entity_type": "string",
        • "external_id": "string"
        }
      ]
    }
}

Retrieve a single Job

Retrieve a Job by its integer primary key. The Job must belong to your account; cross-account IDs return 404.

Authorizations:
BearerAuth
path Parameters
id
required
integer <int64>

Integer primary key of the Job

Responses

Response Schema: application/json
object (Job)

Full representation of a Job used in show/create/update responses (rendered from JobBlueprint :normal).

id
integer <int64>

Internal numeric identifier of the Job

public_id
string

Stable external identifier in the format job-{uuid}

name
string or null
job_status
string or null
job_date
string or null <date>
customer_id
string or null

Customer ID starts with "cus-" then a UUID. Required if account_id is blank.

location_id
string or null

Location ID starts with "loc-" then a UUID. Required if account_id is blank.

site_id
integer or null <int64>

Site's integer primary key.

invoice_amount
number or null <decimal>
currency
string = 3 characters
Default: "USD"

ISO 4217 currency code; defaults to USD.

source
string or null

Free-form provenance label (e.g. servicetitan, manual).

job_type
string or null
business_unit
string or null
description
string or null
scheduled_date
string or null <date>
completed_date
string or null <date>
review_request_opt_out
boolean
Default: false

When true, the job is excluded from review-request campaigns.

visit_count
integer
Default: 0

Number of distinct appointments/visits recorded for this job. Read-only; populated from the source system during ingestion.

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

created_at
string <date-time>
updated_at
string <date-time>
Array of objects (ActiveFieldValue)

Rendered custom-field values from the active_values association. Empty values are filtered out.

Array of objects (JobExternalAlias)

Every external alias registered against this Job, across all of the account's active external systems.

Response samples

Content type
application/json
{
  • "job": {
    • "id": 0,
    • "public_id": "string",
    • "name": "string",
    • "job_status": "string",
    • "job_date": "2019-08-24",
    • "customer_id": "string",
    • "location_id": "string",
    • "site_id": 0,
    • "invoice_amount": 0,
    • "currency": "USD",
    • "source": "string",
    • "job_type": "string",
    • "business_unit": "string",
    • "description": "string",
    • "scheduled_date": "2019-08-24",
    • "completed_date": "2019-08-24",
    • "review_request_opt_out": false,
    • "visit_count": 0,
    • "account_id": "string",
    • "created_at": "2019-08-24T14:15:22Z",
    • "updated_at": "2019-08-24T14:15:22Z",
    • "custom_attributes": [
      • {
        • "name": "string",
        • "label": "string",
        • "value": "string",
        • "type": "string"
        }
      ],
    • "external_aliases": [
      • {
        • "external_system_slug": "string",
        • "external_entity_type": "string",
        • "external_id": "string"
        }
      ]
    }
}

Update a Job

Update mutable Job attributes. Idempotent: applying the same payload twice yields the same final state.

customer_id is immutable after create — any PATCH that attempts to change it is rejected with 422 by a model-level validator.

An optional external_id object can be supplied to register a new ExternalAlias against the Job; the operation is itself idempotent on the alias tuple (re-posting the same external_system + external_id does not create a duplicate alias row).

The shared JobRequestBody schema lists customer_id and location_id as required — that constraint applies to POST only (site_id is optional everywhere; a Job without one is site-less). On PATCH both are optional and any subset of the writable attributes is accepted.

Authorizations:
BearerAuth
path Parameters
id
required
integer <int64>

Integer primary key of the Job

Request Body schema: application/json
required
object
object or null

Optional external-system reference. Used as the primary dedup key on create and as an alias-upsert on update. Both external_system and external_id must be supplied together — providing one without the other returns 422.

Responses

Response Schema: application/json
object (Job)

Full representation of a Job used in show/create/update responses (rendered from JobBlueprint :normal).

id
integer <int64>

Internal numeric identifier of the Job

public_id
string

Stable external identifier in the format job-{uuid}

name
string or null
job_status
string or null
job_date
string or null <date>
customer_id
string or null

Customer ID starts with "cus-" then a UUID. Required if account_id is blank.

location_id
string or null

Location ID starts with "loc-" then a UUID. Required if account_id is blank.

site_id
integer or null <int64>

Site's integer primary key.

invoice_amount
number or null <decimal>
currency
string = 3 characters
Default: "USD"

ISO 4217 currency code; defaults to USD.

source
string or null

Free-form provenance label (e.g. servicetitan, manual).

job_type
string or null
business_unit
string or null
description
string or null
scheduled_date
string or null <date>
completed_date
string or null <date>
review_request_opt_out
boolean
Default: false

When true, the job is excluded from review-request campaigns.

visit_count
integer
Default: 0

Number of distinct appointments/visits recorded for this job. Read-only; populated from the source system during ingestion.

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

created_at
string <date-time>
updated_at
string <date-time>
Array of objects (ActiveFieldValue)

Rendered custom-field values from the active_values association. Empty values are filtered out.

Array of objects (JobExternalAlias)

Every external alias registered against this Job, across all of the account's active external systems.

Request samples

Content type
application/json
{
  • "job": {
    • "customer_id": 0,
    • "location_id": 0,
    • "site_id": 0,
    • "source": "string",
    • "name": "string",
    • "job_type": "string",
    • "job_status": "string",
    • "business_unit": "string",
    • "description": "string",
    • "invoice_amount": 0,
    • "currency": "USD",
    • "scheduled_date": "2019-08-24",
    • "completed_date": "2019-08-24",
    • "job_date": "2019-08-24",
    • "review_request_opt_out": false
    },
  • "external_id": {
    • "external_system": "string",
    • "external_entity_type": "job",
    • "external_id": "string"
    }
}

Response samples

Content type
application/json
{
  • "job": {
    • "id": 0,
    • "public_id": "string",
    • "name": "string",
    • "job_status": "string",
    • "job_date": "2019-08-24",
    • "customer_id": "string",
    • "location_id": "string",
    • "site_id": 0,
    • "invoice_amount": 0,
    • "currency": "USD",
    • "source": "string",
    • "job_type": "string",
    • "business_unit": "string",
    • "description": "string",
    • "scheduled_date": "2019-08-24",
    • "completed_date": "2019-08-24",
    • "review_request_opt_out": false,
    • "visit_count": 0,
    • "account_id": "string",
    • "created_at": "2019-08-24T14:15:22Z",
    • "updated_at": "2019-08-24T14:15:22Z",
    • "custom_attributes": [
      • {
        • "name": "string",
        • "label": "string",
        • "value": "string",
        • "type": "string"
        }
      ],
    • "external_aliases": [
      • {
        • "external_system_slug": "string",
        • "external_entity_type": "string",
        • "external_id": "string"
        }
      ]
    }
}

Queue a Job CSV export

Queue an asynchronous CSV snapshot of every Job in the authenticated account. Returns 202 Accepted with an export object whose token is the public handle for the status-poll and download endpoints. Requires an account API key — the read-only all-access pass cannot queue exports. Exports are capped at a per-account row limit; when the cap drops matching records the status response sets truncated to true.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "export": {
    • "token": "0123456789abcdef0123456789abcdef",
    • "report_type": "site_csv",
    • "status": "pending",
    • "record_count": 0,
    • "truncated": true,
    • "error_message": "string",
    • "expires_at": "2019-08-24T14:15:22Z",
    • "download_url": "string"
    }
}

Poll a Job CSV export

Retrieve the status of a Job CSV export by token. When the export is completed and unexpired, the payload includes a short-lived signed download_url.

Authorizations:
BearerAuth
path Parameters
token
required
string

Export token returned by the queue endpoint — the public handle for one CSV export run.

Responses

Response Schema: application/json
object (CsvExport)

One entity CSV export run. token is the public handle for the status poll and download endpoints. download_url is present only on status responses for a completed, unexpired export; the URL is a short-lived signed link to the CSV file.

token
string
report_type
string
Enum: "site_csv" "job_csv" "employee_csv"
status
string
Enum: "pending" "processing" "completed" "failed" "canceled"
record_count
integer or null

Rows in the generated CSV (set on completion).

truncated
boolean

True when the per-account row cap dropped matching records, so the CSV is a partial snapshot.

error_message
string or null

Generic failure reason. Internal exception detail is deliberately withheld and kept in Liftify's logs.

expires_at
string <date-time>

When the export and its download links expire (7 days after the request).

download_url
string or null

Short-lived signed download URL; only present on status responses when the export is completed and unexpired.

Response samples

Content type
application/json
{
  • "export": {
    • "token": "0123456789abcdef0123456789abcdef",
    • "report_type": "site_csv",
    • "status": "pending",
    • "record_count": 0,
    • "truncated": true,
    • "error_message": "string",
    • "expires_at": "2019-08-24T14:15:22Z",
    • "download_url": "string"
    }
}

Download a completed Job CSV export

Redirects to a time-limited signed object-storage URL for the completed export file.

Authorizations:
BearerAuth
path Parameters
token
required
string

Export token returned by the queue endpoint — the public handle for one CSV export run.

Responses

Response samples

Content type
application/json
{
  • "status": "success",
  • "message": "string",
  • "code": 0,
  • "error": "string"
}

Envelopes

Inbound integration envelopes — batch-ingest external objects (customers, sites, jobs, employees, tag types) via a single-shot or accumulating workflow. ServiceTitan producers may additionally send raw context kinds (contacts, invoices, appointment assignments, business units, job types, memberships) plus technicians/locations aliases; context kinds are stored for wave-0 assembly and never upserted. Every POST creates a new envelope; resubmitting a source_event_id creates a correction linked to the original via original_envelope_id. Object fields are consumed by the wave-0 transformer selected via external_system — slugs with a registered transformer (currently servicetitan) use that vendor's vocabulary; any other slug uses the generic pass-through transformer, which expects canonical Liftify field names (see the GenericEnvelope*Object schemas).

Create an envelope

Create an inbound integration envelope. Two operating modes share this endpoint:

  • single_shot — supply inline objects together with auto_seal: true. In one request the envelope is created, items are appended, the envelope is sealed, and an ingestion worker is enqueued. Responds 202 Accepted with status: "pending".
  • accumulating — omit objects (or send an empty block) and leave auto_seal unset/false. The envelope is created in the building state (201 Created); append items via POST /v1/integrations/envelopes/{id}/items and finalize with POST /v1/integrations/envelopes/{id}/seal.

Corrections: every POST creates a new envelope, even when source_event_id repeats — so a corrected payload can be resubmitted and reprocessed. A repeat submission is a correction that links back to the original request via original_envelope_id (the earliest envelope for that source_event_id); the first submission omits this field.

Each object under objects.<kind> must carry a non-blank external_id — it becomes the item's idempotency key. All other fields on each object are free-form and flow through to the downstream transformer verbatim.

Authorizations:
BearerAuth
Request Body schema: application/json
required
external_system
required
string

Slug of the external system the payload originates from. Must reference an active system in your account. Slugs with a registered transformer (e.g. servicetitan) are normalized with that vendor's vocabulary; any other slug ingests through the generic pass-through transformer using canonical Liftify field names — provisioning an ExternalSystem row is the only onboarding step.

source_event_id
required
string

Caller-supplied identifier of the source event. Not unique — re-posting the same value creates a new envelope (a correction) that links back to the original via original_envelope_id.

schema_version
string
Value: "1"

Payload schema version. Defaults to "1".

mode
string
Enum: "single_shot" "accumulating"

Operating mode. Defaults to single_shot.

auto_seal
boolean

When true, the envelope is sealed and enqueued in the same request (single-shot). Requires at least one object in objects; invalid for accumulating mode.

sync_run_id
integer <int64>

Optional id of the platform sync run this envelope belongs to, echoed from the sync trigger payload. Groups the envelope's ingestion under that run for tracking. Must reference a sync run in your account for the same external_system; an unknown, cross-account, or cross-system id returns 422.

object (EnvelopeObjects)

Inbound objects grouped by kind. Kebab-case kind keys (e.g. appointment-assignments, business-units) are accepted as aliases and normalized to the canonical snake_case kinds before validation. Identity per object is external_id (when present) falling back to the raw source id. Use the ServiceTitan*Object shapes for slugs with a registered transformer (ServiceTitan) and the GenericEnvelope*Object shapes for all other slugs (generic pass-through — canonical field names). The canonical keys (customers, sites, jobs, employees, tag_types) are accepted for every external system; all other keys are vendor vocabulary declared by the system's transformer and are rejected (422) for systems that do not declare them (currently everything except ServiceTitan). For ServiceTitan, technicians / locations are native aliases stored as employee / site items (a ServiceTitan location is the customer's service address), and the context kinds are reference data consumed during wave-0 assembly, never upserted.

Responses

Response Schema: application/json
object (IntegrationEnvelope)

Lifecycle and counter metadata for an inbound integration envelope. The raw per-object payloads are never exposed.

id
integer <int64>

Integer primary key of the envelope.

status
string
Enum: "building" "pending" "processing" "completed" "partially_failed" "failed" "expired" "discarded"

Current lifecycle state.

mode
string
Enum: "single_shot" "accumulating"
schema_version
string

Payload schema version (currently "1").

item_count
integer <int32>

Number of items currently attached to the envelope.

original_envelope_id
integer <int64>

When this envelope is a correction (a resubmission of an earlier source_event_id), the id of the original request's envelope. Omitted on first-time submissions.

received_at
string or null <date-time>
sealed_at
string or null <date-time>
started_at
string or null <date-time>
finished_at
string or null <date-time>
result_summary
object

Per-kind ingestion outcome counts. Present only once populated (omitted while empty).

Request samples

Content type
application/json
Example
{
  • "external_system": "servicetitan",
  • "source_event_id": "evt-9f2c1a7e-44b0-4c2a-9d3e-7a1b2c3d4e5f",
  • "schema_version": "1",
  • "mode": "single_shot",
  • "auto_seal": true,
  • "objects": {
    • "customers": [
      • {
        • "external_id": "st-cust-1001",
        • "firstName": "Jane",
        • "lastName": "Smith"
        }
      ],
    • "jobs": [
      • {
        • "external_id": "st-job-5001",
        • "customerId": "st-cust-1001",
        • "status": "completed"
        }
      ]
    }
}

Response samples

Content type
application/json
{
  • "envelope": {
    • "id": 1024,
    • "status": "pending",
    • "mode": "single_shot",
    • "schema_version": "1",
    • "item_count": 3,
    • "received_at": "2026-06-23T15:04:05Z",
    • "sealed_at": "2026-06-23T15:04:06Z",
    • "started_at": null,
    • "finished_at": null
    }
}

Retrieve a single envelope

Retrieve an envelope by its integer primary key. The envelope must belong to your account; cross-account IDs return 404.

Authorizations:
BearerAuth
path Parameters
id
required
integer <int64>

Integer primary key of the envelope

Responses

Response Schema: application/json
object (IntegrationEnvelope)

Lifecycle and counter metadata for an inbound integration envelope. The raw per-object payloads are never exposed.

id
integer <int64>

Integer primary key of the envelope.

status
string
Enum: "building" "pending" "processing" "completed" "partially_failed" "failed" "expired" "discarded"

Current lifecycle state.

mode
string
Enum: "single_shot" "accumulating"
schema_version
string

Payload schema version (currently "1").

item_count
integer <int32>

Number of items currently attached to the envelope.

original_envelope_id
integer <int64>

When this envelope is a correction (a resubmission of an earlier source_event_id), the id of the original request's envelope. Omitted on first-time submissions.

received_at
string or null <date-time>
sealed_at
string or null <date-time>
started_at
string or null <date-time>
finished_at
string or null <date-time>
result_summary
object

Per-kind ingestion outcome counts. Present only once populated (omitted while empty).

Response samples

Content type
application/json
{
  • "envelope": {
    • "id": 1024,
    • "status": "pending",
    • "mode": "single_shot",
    • "schema_version": "1",
    • "item_count": 3,
    • "received_at": "2026-06-23T15:04:05Z",
    • "sealed_at": "2026-06-23T15:04:06Z",
    • "started_at": null,
    • "finished_at": null
    }
}

Discard an envelope

Discard a building envelope and destroy its items. Only envelopes still in the building state can be discarded — once sealed (or in any other state) the envelope is locked and this returns 409.

Authorizations:
BearerAuth
path Parameters
id
required
integer <int64>

Integer primary key of the envelope

Responses

Response samples

Content type
application/json
{
  • "status": "success",
  • "message": "string",
  • "code": 0,
  • "error": "string"
}

Append items to an envelope

Append objects to a building envelope. Items are upserted idempotently on (envelope, kind, external_id) — re-posting the same external_id updates the stored payload rather than creating a duplicate.

Only building envelopes accept items; appending to a sealed (or otherwise locked) envelope returns 409. The request must contain at least one object, and every object must carry a non-blank external_id.

Authorizations:
BearerAuth
path Parameters
id
required
integer <int64>

Integer primary key of the envelope

Request Body schema: application/json
required
required
object (EnvelopeObjects)

Inbound objects grouped by kind. Kebab-case kind keys (e.g. appointment-assignments, business-units) are accepted as aliases and normalized to the canonical snake_case kinds before validation. Identity per object is external_id (when present) falling back to the raw source id. Use the ServiceTitan*Object shapes for slugs with a registered transformer (ServiceTitan) and the GenericEnvelope*Object shapes for all other slugs (generic pass-through — canonical field names). The canonical keys (customers, sites, jobs, employees, tag_types) are accepted for every external system; all other keys are vendor vocabulary declared by the system's transformer and are rejected (422) for systems that do not declare them (currently everything except ServiceTitan). For ServiceTitan, technicians / locations are native aliases stored as employee / site items (a ServiceTitan location is the customer's service address), and the context kinds are reference data consumed during wave-0 assembly, never upserted.

Array of ServiceTitanCustomerObject (object) or GenericEnvelopeCustomerObject (object)
Array of ServiceTitanSiteObject (object) or GenericEnvelopeSiteObject (object)
Array of ServiceTitanJobObject (object) or GenericEnvelopeJobObject (object)
Array of ServiceTitanEmployeeObject (object) or GenericEnvelopeEmployeeObject (object)
Array of objects (ServiceTitanTagTypeObject)
Array of objects (ServiceTitanEmployeeObject)

ServiceTitan settings/v2/technicians entries, stored as employee items.

Array of objects (ServiceTitanSiteObject)

ServiceTitan crm/v2/locations entries (true service addresses), stored as site items.

Array of objects (ServiceTitanContactObject)
Array of objects (ServiceTitanInvoiceObject)
Array of objects (ServiceTitanAppointmentAssignmentObject)
Array of objects (ServiceTitanBusinessUnitObject)
Array of objects (ServiceTitanJobTypeObject)
Array of objects (ServiceTitanMembershipObject)

Responses

Response Schema: application/json
object (IntegrationEnvelope)

Lifecycle and counter metadata for an inbound integration envelope. The raw per-object payloads are never exposed.

id
integer <int64>

Integer primary key of the envelope.

status
string
Enum: "building" "pending" "processing" "completed" "partially_failed" "failed" "expired" "discarded"

Current lifecycle state.

mode
string
Enum: "single_shot" "accumulating"
schema_version
string

Payload schema version (currently "1").

item_count
integer <int32>

Number of items currently attached to the envelope.

original_envelope_id
integer <int64>

When this envelope is a correction (a resubmission of an earlier source_event_id), the id of the original request's envelope. Omitted on first-time submissions.

received_at
string or null <date-time>
sealed_at
string or null <date-time>
started_at
string or null <date-time>
finished_at
string or null <date-time>
result_summary
object

Per-kind ingestion outcome counts. Present only once populated (omitted while empty).

Request samples

Content type
application/json
{
  • "objects": {
    • "customers": [
      • {
        • "external_id": "st-cust-1002",
        • "firstName": "John",
        • "lastName": "Doe"
        }
      ],
    • "sites": [
      • {
        • "external_id": "st-site-3001",
        • "addressLine1": "123 Main St"
        }
      ]
    }
}

Response samples

Content type
application/json
{
  • "envelope": {
    • "id": 1024,
    • "status": "pending",
    • "mode": "single_shot",
    • "schema_version": "1",
    • "item_count": 3,
    • "received_at": "2026-06-23T15:04:05Z",
    • "sealed_at": "2026-06-23T15:04:06Z",
    • "started_at": null,
    • "finished_at": null
    }
}

Seal an envelope

Seal a building envelope, transitioning it to pending and enqueuing the ingestion worker. Responds 202 Accepted.

Idempotency: re-sealing an envelope already past building (e.g. pending, processing, or a terminal-success state) is a no-op success (202). Sealing an expired or discarded envelope returns 409; sealing an envelope with no items returns 422.

Authorizations:
BearerAuth
path Parameters
id
required
integer <int64>

Integer primary key of the envelope

Responses

Response samples

Content type
application/json
{
  • "envelope": {
    • "id": 1024,
    • "status": "pending",
    • "mode": "single_shot",
    • "schema_version": "1",
    • "item_count": 3,
    • "received_at": "2026-06-23T15:04:05Z",
    • "sealed_at": "2026-06-23T15:04:06Z",
    • "started_at": null,
    • "finished_at": null
    }
}

Report a sync run's outcome

Report-back endpoint for the platform-triggered sync processor. One POST per sync run:

  • status: "completed" advances the tenant's export-feed cursors — only the feeds present under cursors are advanced; an omitted feed's old cursor replays on the next run — stamps last_synced_at, and completes the sync run.
  • status: "failed" marks the sync run failed with error_message and leaves all cursors untouched.

Idempotency: a run already in a terminal state answers 200 without changes, so duplicate deliveries are harmless. The sync run must belong to your account; cross-account ids return 404.

Authorizations:
BearerAuth
path Parameters
id
required
integer <int64>

Integer id of the sync run (from the sync trigger payload)

Request Body schema: application/json
required
status
required
string
Enum: "completed" "failed"

Outcome of the processor run.

object

Per-feed continuation cursors to advance (feed name => continueFrom). Only supplied feeds are advanced.

error_message
string

Human-readable failure detail (used with the failed status).

backlogged_feeds
Array of strings

Feeds that hit their per-run drain cap and are still catching up (used with the completed status; persisted on the sync run's metadata).

c4_overflow
integer >= 0

Count of customer creates deferred past the processor's per-run contact fan-out cap (used with the completed status; persisted on the sync run's metadata).

Responses

Response Schema: application/json
object
id
integer <int64>
status
string
Enum: "pending" "processing" "completed" "skipped" "failed"

Request samples

Content type
application/json
Example
{
  • "status": "completed",
  • "cursors": {
    • "jobs": "2026-07-09T17:00:00Z",
    • "invoices": "2026-07-09T16:58:12Z"
    },
  • "backlogged_feeds": [
    • "invoices"
    ],
  • "c4_overflow": 0
}

Response samples

Content type
application/json
{
  • "sync_run": {
    • "id": 0,
    • "status": "pending"
    }
}

Employees

List employees

Retrieve a paginated list of Employees for the authenticated account.

Filter combinations:

  • By external identifier — pass external_system (slug) together with external_id to look up the Employee that maps to that external record. Optionally narrow further with external_entity_type (defaults to employee). When no alias resolves the response is 200 OK with an empty employees list (parity with Sites listing).
  • By linked user — pass user_id (the Liftify internal User integer id) to return the Employee that is linked to that User (or an empty list).
  • By location — pass location (the public loc- identifier) to restrict to Employees at that Location, via active JobEmployee assignments or direct location assignment.
  • By name — pass name to substring-match (case-insensitive) against first_name, last_name, or the concatenated full name.

Cross-account ids supplied for location return an empty result set, never records owned by another account.

Authorizations:
BearerAuth
query Parameters
external_system
string

Slug of the external system (e.g. servicetitan). Combine with external_id.

external_id
string

External identifier to look up. Combine with external_system.

external_entity_type
string
Default: "employee"

External entity type (defaults to employee when external_system/external_id are supplied). Normalized to lowercase.

user_id
integer <int64>

Filter to the Employee linked to the supplied Liftify User integer id.

location
string

Filter to Employees at the Location matching the supplied public loc-... identifier, whether via active JobEmployee assignments or direct location assignment.

name
string

Case-insensitive substring matched against first_name, last_name, and the concatenated full name.

page
integer >= 1
Default: 1

Page number for pagination

size
integer [ 1 .. 1000 ]
Default: 100

Number of items per page

Responses

Response Schema: application/json
total_count
integer <int32>

Total number of employees matching the filter criteria

total_pages
integer <int32>

Total number of pages available

current_page
integer <int32>

Current page number

Array of objects (EmployeeListItem)

Response samples

Content type
application/json
{
  • "total_count": 1,
  • "total_pages": 1,
  • "current_page": 1,
  • "employees": [
    • {
      • "id": 42,
      • "employee_id": "emp-2c1bd9e6-7c52-4f08-9b86-2c0e3a8a7c11",
      • "first_name": "Pat",
      • "last_name": "Lee",
      • "email": "[email protected]",
      • "active": true,
      • "title": "Senior Technician"
      }
    ]
}

Create or dedupe an Employee

Create an Employee, routed through the EmployeeResolver pipeline:

  1. If external_id.external_system + external_id.external_id are supplied and already map to an Employee in this account, that Employee is returned (200 OK, no new alias row created).
  2. Otherwise, if employee.user_id is supplied and an Employee in this account is already linked to that User, that Employee is returned (200 OK).
  3. Otherwise, if first_name + last_name exactly match an active Employee in this account (case-insensitive), that Employee is returned (200 OK).
  4. Otherwise the intelligent matcher attempts a nickname / fuzzy / AI-disambiguation match — high-confidence hits are returned as 200 OK.
  5. Otherwise a new Employee is inserted; when external_id is supplied an ExternalAlias row is registered in the same transaction (201 Created).

Employees are account-isolated: identical external_id tuples across two different accounts produce two distinct Employees.

Authorizations:
BearerAuth
Request Body schema: application/json
required
object
object or null

Optional external-system reference. Used as the primary dedup key on create and as an alias-upsert on update. Both external_system and external_id must be supplied together — providing one without the other returns 422.

Responses

Response Schema: application/json
object (Employee)

Full representation of an Employee used in show/create/update responses (rendered from EmployeeBlueprint :normal).

id
integer <int64>

Internal numeric identifier of the Employee

employee_id
string

Stable public identifier in the format emp-{uuid}

first_name
string
last_name
string
email
string or null
active
boolean
title
string or null
phone
string or null
source
string or null

Free-form provenance label (e.g. api, app, servicetitan).

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

user_id
integer or null <int64>

Linked Liftify User integer id (or null when no user link).

created_at
string <date-time>
updated_at
string <date-time>
merged_into_id
integer or null <int64>

Set only on an Employee that has been merged into another as a duplicate; it is the integer id of the Employee that absorbed this one. null on every live Employee. Merged Employees are excluded from the listing endpoint and cannot be updated, but GET /api/v1/employees/{id} still resolves them so an integration holding the merged id can follow this pointer to the surviving record.

merged_at
string or null <date-time>

When this Employee was merged into merged_into_id; null on every live Employee.

Array of objects (EmployeeExternalAlias)

Every external alias registered against this Employee, across all of the account's active external systems.

Response Schema: application/json
object (Employee)

Full representation of an Employee used in show/create/update responses (rendered from EmployeeBlueprint :normal).

id
integer <int64>

Internal numeric identifier of the Employee

employee_id
string

Stable public identifier in the format emp-{uuid}

first_name
string
last_name
string
email
string or null
active
boolean
title
string or null
phone
string or null
source
string or null

Free-form provenance label (e.g. api, app, servicetitan).

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

user_id
integer or null <int64>

Linked Liftify User integer id (or null when no user link).

created_at
string <date-time>
updated_at
string <date-time>
merged_into_id
integer or null <int64>

Set only on an Employee that has been merged into another as a duplicate; it is the integer id of the Employee that absorbed this one. null on every live Employee. Merged Employees are excluded from the listing endpoint and cannot be updated, but GET /api/v1/employees/{id} still resolves them so an integration holding the merged id can follow this pointer to the surviving record.

merged_at
string or null <date-time>

When this Employee was merged into merged_into_id; null on every live Employee.

Array of objects (EmployeeExternalAlias)

Every external alias registered against this Employee, across all of the account's active external systems.

Request samples

Content type
application/json
{
  • "employee": {
    • "first_name": "string",
    • "last_name": "string",
    • "email": "string",
    • "phone": "string",
    • "title": "string",
    • "department": "string",
    • "source": "string",
    • "active": true,
    • "user_id": 0
    },
  • "external_id": {
    • "external_system": "string",
    • "external_entity_type": "employee",
    • "external_id": "string"
    }
}

Response samples

Content type
application/json
{
  • "employee": {
    • "id": 0,
    • "employee_id": "string",
    • "first_name": "string",
    • "last_name": "string",
    • "email": "string",
    • "active": true,
    • "title": "string",
    • "phone": "string",
    • "source": "string",
    • "account_id": "string",
    • "user_id": 0,
    • "created_at": "2019-08-24T14:15:22Z",
    • "updated_at": "2019-08-24T14:15:22Z",
    • "merged_into_id": 0,
    • "merged_at": "2019-08-24T14:15:22Z",
    • "external_aliases": [
      • {
        • "external_system_slug": "string",
        • "external_entity_type": "string",
        • "external_id": "string"
        }
      ]
    }
}

Retrieve a single Employee

Retrieve an Employee by its integer primary key. The Employee must belong to your account; cross-account IDs return 404.

This endpoint also resolves an Employee that has been merged into another as a duplicate — it is the only endpoint that does. The response carries merged_into_id / merged_at so a caller holding the merged id can follow the pointer to the surviving Employee. Merged Employees are omitted from the listing endpoint and rejected by the update endpoint.

Authorizations:
BearerAuth
path Parameters
id
required
integer <int64>

Integer primary key of the Employee

Responses

Response Schema: application/json
object (Employee)

Full representation of an Employee used in show/create/update responses (rendered from EmployeeBlueprint :normal).

id
integer <int64>

Internal numeric identifier of the Employee

employee_id
string

Stable public identifier in the format emp-{uuid}

first_name
string
last_name
string
email
string or null
active
boolean
title
string or null
phone
string or null
source
string or null

Free-form provenance label (e.g. api, app, servicetitan).

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

user_id
integer or null <int64>

Linked Liftify User integer id (or null when no user link).

created_at
string <date-time>
updated_at
string <date-time>
merged_into_id
integer or null <int64>

Set only on an Employee that has been merged into another as a duplicate; it is the integer id of the Employee that absorbed this one. null on every live Employee. Merged Employees are excluded from the listing endpoint and cannot be updated, but GET /api/v1/employees/{id} still resolves them so an integration holding the merged id can follow this pointer to the surviving record.

merged_at
string or null <date-time>

When this Employee was merged into merged_into_id; null on every live Employee.

Array of objects (EmployeeExternalAlias)

Every external alias registered against this Employee, across all of the account's active external systems.

Response samples

Content type
application/json
{
  • "employee": {
    • "id": 0,
    • "employee_id": "string",
    • "first_name": "string",
    • "last_name": "string",
    • "email": "string",
    • "active": true,
    • "title": "string",
    • "phone": "string",
    • "source": "string",
    • "account_id": "string",
    • "user_id": 0,
    • "created_at": "2019-08-24T14:15:22Z",
    • "updated_at": "2019-08-24T14:15:22Z",
    • "merged_into_id": 0,
    • "merged_at": "2019-08-24T14:15:22Z",
    • "external_aliases": [
      • {
        • "external_system_slug": "string",
        • "external_entity_type": "string",
        • "external_id": "string"
        }
      ]
    }
}

Update an Employee

Update mutable Employee attributes. Idempotent: applying the same payload twice yields the same final state and does not republish EmployeeUpdated.

An optional external_id object can be supplied to register a new ExternalAlias against the Employee; the operation is itself idempotent on the alias tuple (re-posting the same external_system + external_id does not create a duplicate alias row).

Authorizations:
BearerAuth
path Parameters
id
required
integer <int64>

Integer primary key of the Employee

Request Body schema: application/json
required
object
object or null

Optional external-system reference. Used as the primary dedup key on create and as an alias-upsert on update. Both external_system and external_id must be supplied together — providing one without the other returns 422.

Responses

Response Schema: application/json
object (Employee)

Full representation of an Employee used in show/create/update responses (rendered from EmployeeBlueprint :normal).

id
integer <int64>

Internal numeric identifier of the Employee

employee_id
string

Stable public identifier in the format emp-{uuid}

first_name
string
last_name
string
email
string or null
active
boolean
title
string or null
phone
string or null
source
string or null

Free-form provenance label (e.g. api, app, servicetitan).

account_id
string

Account ID starts with "acc-" then a UUID. Required if location_name is present.

user_id
integer or null <int64>

Linked Liftify User integer id (or null when no user link).

created_at
string <date-time>
updated_at
string <date-time>
merged_into_id
integer or null <int64>

Set only on an Employee that has been merged into another as a duplicate; it is the integer id of the Employee that absorbed this one. null on every live Employee. Merged Employees are excluded from the listing endpoint and cannot be updated, but GET /api/v1/employees/{id} still resolves them so an integration holding the merged id can follow this pointer to the surviving record.

merged_at
string or null <date-time>

When this Employee was merged into merged_into_id; null on every live Employee.

Array of objects (EmployeeExternalAlias)

Every external alias registered against this Employee, across all of the account's active external systems.

Request samples

Content type
application/json
{
  • "employee": {
    • "first_name": "string",
    • "last_name": "string",
    • "email": "string",
    • "phone": "string",
    • "title": "string",
    • "department": "string",
    • "source": "string",
    • "active": true,
    • "user_id": 0
    },
  • "external_id": {
    • "external_system": "string",
    • "external_entity_type": "employee",
    • "external_id": "string"
    }
}

Response samples

Content type
application/json
{
  • "employee": {
    • "id": 0,
    • "employee_id": "string",
    • "first_name": "string",
    • "last_name": "string",
    • "email": "string",
    • "active": true,
    • "title": "string",
    • "phone": "string",
    • "source": "string",
    • "account_id": "string",
    • "user_id": 0,
    • "created_at": "2019-08-24T14:15:22Z",
    • "updated_at": "2019-08-24T14:15:22Z",
    • "merged_into_id": 0,
    • "merged_at": "2019-08-24T14:15:22Z",
    • "external_aliases": [
      • {
        • "external_system_slug": "string",
        • "external_entity_type": "string",
        • "external_id": "string"
        }
      ]
    }
}

Queue an Employee CSV export

Queue an asynchronous CSV snapshot of every Employee in the authenticated account. Returns 202 Accepted with an export object whose token is the public handle for the status-poll and download endpoints. Requires an account API key — the read-only all-access pass cannot queue exports. Exports are capped at a per-account row limit; when the cap drops matching records the status response sets truncated to true.

Authorizations:
BearerAuth

Responses

Response samples

Content type
application/json
{
  • "export": {
    • "token": "0123456789abcdef0123456789abcdef",
    • "report_type": "site_csv",
    • "status": "pending",
    • "record_count": 0,
    • "truncated": true,
    • "error_message": "string",
    • "expires_at": "2019-08-24T14:15:22Z",
    • "download_url": "string"
    }
}

Poll an Employee CSV export

Retrieve the status of an Employee CSV export by token. When the export is completed and unexpired, the payload includes a short-lived signed download_url.

Authorizations:
BearerAuth
path Parameters
token
required
string

Export token returned by the queue endpoint — the public handle for one CSV export run.

Responses

Response Schema: application/json
object (CsvExport)

One entity CSV export run. token is the public handle for the status poll and download endpoints. download_url is present only on status responses for a completed, unexpired export; the URL is a short-lived signed link to the CSV file.

token
string
report_type
string
Enum: "site_csv" "job_csv" "employee_csv"
status
string
Enum: "pending" "processing" "completed" "failed" "canceled"
record_count
integer or null

Rows in the generated CSV (set on completion).

truncated
boolean

True when the per-account row cap dropped matching records, so the CSV is a partial snapshot.

error_message
string or null

Generic failure reason. Internal exception detail is deliberately withheld and kept in Liftify's logs.

expires_at
string <date-time>

When the export and its download links expire (7 days after the request).

download_url
string or null

Short-lived signed download URL; only present on status responses when the export is completed and unexpired.

Response samples

Content type
application/json
{
  • "export": {
    • "token": "0123456789abcdef0123456789abcdef",
    • "report_type": "site_csv",
    • "status": "pending",
    • "record_count": 0,
    • "truncated": true,
    • "error_message": "string",
    • "expires_at": "2019-08-24T14:15:22Z",
    • "download_url": "string"
    }
}

Download a completed Employee CSV export

Redirects to a time-limited signed object-storage URL for the completed export file.

Authorizations:
BearerAuth
path Parameters
token
required
string

Export token returned by the queue endpoint — the public handle for one CSV export run.

Responses

Response samples

Content type
application/json
{
  • "status": "success",
  • "message": "string",
  • "code": 0,
  • "error": "string"
}