ProLine Partner API (1.0)

Download OpenAPI specification:

ProLine support: support@proline.app

Read and write a ProLine company's projects, contacts, events, activities and files on behalf of an integration partner.

Overview

The Partner API lets an approved integration partner read and write data in a ProLine company that has shared its company key with the partner. Every request is a POST with a JSON body to a fixed path under https://api.proline.app/v1/, authenticated with two keys (see Authentication). Responses are JSON.

Endpoints are grouped by the record they work on. Lists are paged; finds return a single record (or an empty array when nothing matches); edits create or update; imports create records in bulk-friendly shapes and are idempotent on an external_id you supply.

Authentication

Two keys travel as request headers on every call:

Header What it identifies Where it comes from
PARTNER_KEY Your integration. One key per partner, used against every company you serve. Issued by ProLine when your application is approved.
COMPANY_KEY The ProLine company the request acts on. The company's admin copies it from Settings → Integrations → ProLine API and gives it to you.

A request with a missing or unknown key is refused with 401. Rate limits (below) are per company key, so one slow integration never affects another company's.

Keep both keys server-side. The company key in particular grants access to that company's customer data; treat it like a password.

Rate limits

Limits are enforced per company key and per path, as a fixed interval between calls. A call inside the interval is refused with 429 and a body of { "error": "Rate limit exceeded for <path>. Try again in <n> seconds." }.

Paths Interval
/v1/find/contact, /v1/edit/contact, /v1/import/contact 1 call per 3 seconds
/v1/import/file 1 call per second
/v1/import/activity 1 call per 2 seconds
/v1/import/tags 1 call per 10 seconds
Every other path 1 call per 5 seconds

Lists return up to 100 records per page; a larger limit is clamped to 100.

Response shapes

Two conventions exist, for historical reasons, and each endpoint says which it follows:

Direct. The newer endpoints (every /v1/list/* path, /v1/find/activity) answer with the record data as the whole body and a meaningful HTTP status: 200 on success, 400 for an invalid request, 401/403 for a key problem, 429 when rate limited.

Wrapped. The original endpoints answer 200 whatever happened and wrap the result in a fixed envelope:

{
  "isBase64Encoded": false,
  "statusCode": 200,
  "statusDescription": "200 OK",
  "headers": { "Content-Type": "application/json" },
  "body": { ... }
}

The result, including any error the backend reported, is in body. Check body for an error key before treating the call as successful. Only errors raised before the request reaches the backend (an unknown key, a rate limit, an invalid JSON body, an unknown body key) carry a real HTTP status and a flat { "error": "..." } body.

Errors

Status Meaning Body
400 Invalid JSON, a body key the endpoint does not accept (Invalid key found: "<key>"), or a missing required key. { "error": "..." }
401 Unknown or inactive partner key, or unknown company key. { "error": "..." }
403 The partner key is not permitted to call this endpoint. { "error": "Permission Denied" }
404 Unknown path. { "error": "..." }
429 Rate limited (see Rate limits). { "error": "..." }

Backend validation errors on the direct endpoints use { "success": false, "error": { "code": "BAD_REQUEST", "message": "..." } }.

Identifiers

Ids in responses are ProLine ids (opaque strings). Where a company was migrated from the original ProLine platform, requests may also use that platform's legacy ids for the same records, and activity records of migrated data still report their legacy ids. Treat every id as an opaque string.

Webhooks

ProLine can call your endpoint when records change. Outbound webhooks are configured by the company in ProLine, not through this API; see ProLine Webhooks.

Projects

List projects

Pages through every project in the company, newest first, with its stage, contacts, budget figures and status dates. Filter by creation date with created_after / created_before (both inclusive). Pages are limited to 100 rows and to an offset of 10,000 rows; beyond that, or when a page would read more than the per-call budget, the call answers 400 with a { "status": "error", "message": "..." } body and you should narrow the date window. total is exact up to 4,000 matching rows and a lower bound past that; has_more is authoritative. project_notes is returned as plain text.

Response shape: direct (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
page
integer >= 1
Default: 1

1-based page number.

limit
integer [ 1 .. 100 ]
Default: 100

Rows per page. A value above 100 is clamped to 100.

created_after
string or null <date-time>

Only projects created at or after this instant. A full ISO 8601 datetime with timezone (for example 2026-09-01T00:00:00Z); a date alone is refused with 400. null means no bound.

created_before
string or null <date-time>

Only projects created at or before this instant. Same format as created_after; must not be earlier than it.

Responses

Request samples

Content type
application/json
{
  • "page": 1,
  • "limit": 50,
  • "created_after": "2026-09-01T00:00:00Z"
}

Response samples

Content type
application/json
{
  • "page": 1,
  • "limit": 50,
  • "total": 812,
  • "has_more": true,
  • "results": [
    ]
}

Find a project

Looks up one project and returns the first match. The keys are tried in this order: project_id, then project_external_id, then contact_id (the first project whose main contact is that contact), then the address fields together. project_stage and project_status do not find a project on their own; they filter the one that was found, so a project in another stage or status answers [].

body is an array with one project, or an empty array when nothing matched (there is no not-found error). An id that does not resolve, in the lookup or in a filter, also answers [].

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
project_id
string (ProLineId)

A ProLine record id. Opaque; in requests, a legacy id for the same record is also accepted.

project_external_id
string

The project's external_id (the newest project with that value); a legacy project id is also accepted here.

contact_id
string

Main contact id; a legacy id is also accepted.

project_address1
string

Street line, used with project_city, project_state and project_zip only when no id matched.

project_address2
string

Accepted and ignored.

project_city
string
project_state
string
project_zip
string
project_stage
string

Stage id (see /v1/list/project_stages). Filter: the found project must be in this stage.

project_status
string
Enum: "lead" "inspection" "open" "disqualified" "lost" "won" "unfunded" "funded" "complete" "closed"

Filter: the found project's stage must have this status. The underlying value, not the label the response returns in status.

Responses

Request samples

Content type
application/json
{
  • "project_external_id": "CRM-10042"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": [
    ]
}

Create or update a project

Updates an existing project and its main contact, or creates both, in one call, and optionally schedules an event on the project.

Create or update is decided in this order: project_id; otherwise the one project whose external_id matches (two or more matches are rejected, send project_id); otherwise, when edit_contact_latest is true, the contact's newest open project; otherwise a new project is created. external_id_new renames the external id and is refused when another project holds it.

The contact is resolved by contact_id, then contact_phone, then contact_email, then the existing project's main contact, then an unsaved draft with that phone; otherwise a contact is created, which requires contact_fname or contact_name.

Omitted or blank fields are left unchanged on both the project and the contact, so a value cannot be cleared through this endpoint. The one exception is project_tags: when present it replaces the whole tag list, and "" or [] clears it. A new project with no project_stage goes into the first stage of the first project pipeline. Ids that do not resolve (project_assign, project_category, project_location, ...) are dropped and the call still succeeds.

Side effects: a contact may be created, default folders are created on a new project, field changes are logged as activities and project webhooks fire. An event scheduling failure never fails the call; event_scheduled is then "null". An event_external_id held by an event on another project is skipped.

Rejections arrive inside body.response with error: true (the outer status is still "success"). When the company's automations are paused, body is { "ok": true } and nothing is written.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
project_id
string (ProLineId)

A ProLine record id. Opaque; in requests, a legacy id for the same record is also accepted.

external_id
string

Your own id for the project; lookup key when project_id is absent.

external_id_new
string

New external_id for the project.

edit_contact_latest
string

true, 1, yes or on: when no project matched by id, edit the contact's newest open project instead of creating one.

project_name
string
project_address1
string
project_address2
string
project_city
string
project_state
string
project_zip
string
project_notes
string
project_location
string

Location id.

project_assign
string

Team member id for the sales assignment.

project_inside_sales
string

Team member id.

project_production
string

Team member id.

project_accounting
string

Team member id.

project_category
string

Project category id.

project_type
string

Project type id.

project_stage
string

Stage id (see /v1/list/project_stages). On create, defaults to the first stage of the first project pipeline; a company with no stage at all rejects the call.

project_tags
Array of strings or string

Tag ids, as an array or a comma-separated string. Replaces the whole tag list; "" or [] clears it.

project_custom_field_1
string

Written to the first legacy project custom field slot. Skipped when the company has no such field.

project_custom_field_2
string
project_custom_field_3
string
project_custom_field_4
string
project_custom_field_5
string
project_custom_field_6
string
project_custom_field_7
string
project_services
string

Accepted and ignored.

project_area
string

Accepted and ignored.

contact_campaign
string

Campaign id for attribution.

contact_id
string

Main contact id; a legacy id is also accepted.

contact_fname
string

First name. One of this or contact_name is required when creating the contact.

contact_name
string

Full name, used when creating the contact without contact_fname.

contact_phone
string

Any format; also a contact lookup key.

contact_email
string

Also a contact lookup key.

contact_type
string

Contact type id.

contact_lead_source
string

Lead source id.

contact_tags
Array of strings or string

Contact tag ids, as an array or a comma-separated string.

event_external_id
string

Your id for the event to schedule or reschedule after the edit.

event_external_id_new
string

New external id for that event.

event_start_date
string

Event start. A string with a UTC offset is an absolute instant; one without is wall-clock time in event_time_zone, or UTC when none is given.

event_type
string

Event type id (see /v1/list/event_types).

event_time_zone
string

ProLine time zone value, legacy option key or IANA name for a naive event_start_date.

event_skip_external_create
string

true, 1, yes or on: exclude the event from external calendar sync. The event is still created.

Responses

Request samples

Content type
application/json
{
  • "external_id": "CRM-10042",
  • "contact_fname": "Dana",
  • "contact_phone": "+15125550142",
  • "project_name": "Ortiz re-roof",
  • "project_address1": "12 Elm St",
  • "project_city": "Austin",
  • "project_state": "TX",
  • "project_zip": "78701",
  • "project_stage": "k97f2x1n0cq8vb3d5t6hm4ws2e7a9zq1",
  • "project_tags": [
    ],
  • "event_start_date": "2026-10-15T14:00:00",
  • "event_time_zone": "central",
  • "event_type": "et4c8h2k6sq9nb3v1d7xm5wr0te4a8yc"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

List project stages

Returns the stages of the company's project pipelines, grouped by pipeline in pipeline order. The id is what /v1/find/project returns as stage_id, and what project_stage takes on /v1/edit/project (/v1/list/projects returns the stage name only). Archived stages are not listed.

Response shape: direct (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
object

Empty, or {}. Any key is refused with 400.

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
[
  • {
    },
  • {
    }
]

Jobs

List jobs

Pages through every job in the company, newest first, with its project, service and stage. Filter by creation date with created_after / created_before (both inclusive). Pages are limited to 100 rows and to an offset of 10,000 rows; beyond that, or when a page would read more than the per-call budget, the call answers 400 with a { "status": "error", "message": "..." } body and you should narrow the date window. total is exact up to 4,000 matching rows and a lower bound past that; has_more is authoritative. job_description is returned as plain text.

Response shape: direct (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
page
integer >= 1
Default: 1

1-based page number.

limit
integer [ 1 .. 100 ]
Default: 100

Rows per page. A value above 100 is clamped to 100.

created_after
string or null <date-time>

Only jobs created at or after this instant. A full ISO 8601 datetime with timezone (for example 2026-09-01T00:00:00Z); a date alone is refused with 400. null means no bound.

created_before
string or null <date-time>

Only jobs created at or before this instant. Same format as created_after; must not be earlier than it.

Responses

Request samples

Content type
application/json
{
  • "limit": 100
}

Response samples

Content type
application/json
{
  • "page": 1,
  • "limit": 100,
  • "total": 3,
  • "has_more": false,
  • "results": [
    ]
}

Contacts

Find a contact

Looks up one contact by id, phone, email or address and returns the first match. The keys are tried in that order: contact_id, then contact_phone, then contact_email, then the address fields together; the first key that matches wins and the rest are not consulted. Archived contacts and contacts of other companies are not matched.

body is an array with one contact, or an empty array when nothing matched (there is no not-found error). An empty request body returns the empty array.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
contact_id
string (ProLineId)

A ProLine record id. Opaque; in requests, a legacy id for the same record is also accepted.

contact_phone
string

Any format; the number is normalized before matching. A saved contact is preferred over an unsaved draft with the same number.

contact_email
string

Matched exactly.

contact_address1
string

Street line, used with contact_city, contact_state and contact_zip only when no id, phone or email matched.

contact_address2
string

Accepted and ignored.

contact_city
string
contact_state
string
contact_zip
string

Responses

Request samples

Content type
application/json
{
  • "contact_phone": "+15125550142"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": [
    ]
}

Create or update a contact

Updates an existing contact, or creates one when no match is found. The contact is matched by contact_id first; otherwise by contact_phone, against the newest saved contact with the same number, then against an unsaved draft with that number (the draft becomes the contact); otherwise a new contact is created, which requires contact_fname.

Omitted or blank fields are left unchanged, so a value cannot be cleared through this endpoint. external_id is stored on create only and ignored on update.

Side effects: field changes are logged as activities, the company's contact webhooks fire, lead-source attribution on the contact's projects is synced, and a new contact is given the next contact number.

Rejections arrive inside body.response with error: true (the outer status is still "success"): a missing first name on create, a phone number already held by another contact (that contact's id is returned), and others. When the company's automations are paused, body is { "ok": true } and nothing is written.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
contact_id
string (ProLineId)

A ProLine record id. Opaque; in requests, a legacy id for the same record is also accepted.

contact_fname
string

First name. Required when creating.

contact_lname
string
contact_organization
string

Organization id.

contact_phone
string

Any format; normalized to E.164. Also the lookup key when contact_id is absent. Refused when another contact already has the number.

contact_email
string
contact_address1
string
contact_address2
string
contact_city
string
contact_state
string
contact_zip
string
contact_notes
string
contact_type
string

Contact type id. Validated together with lead_source.

lead_source
string

Lead source id. Validated together with contact_type.

time_zone
string

ProLine time zone value (central, mountain, pacific, eastern, hawaii, arizona, atlantic, ...), a legacy option key such as central_standard_time, or an IANA name.

external_id
string

Stored as the contact's legacy id on create only; ignored on update.

custom_field_1
string

Written to the first legacy custom field slot. Skipped when the company has no such field.

custom_field_2
string
custom_field_3
string
contact_tags
string

Accepted and ignored.

contact_campaign_id
string

Accepted and ignored.

custom_date_1
string

Accepted and ignored.

custom_date_2
string

Accepted and ignored.

custom_date_3
string

Accepted and ignored.

Responses

Request samples

Content type
application/json
{
  • "contact_fname": "Dana",
  • "contact_lname": "Ortiz",
  • "contact_phone": "512-555-0142",
  • "contact_email": "dana@example.com",
  • "contact_state": "TX",
  • "custom_field_1": "Referral: HOA"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

Quotes

List quotes

Pages through the company's quotes, newest first, with the project and contact each belongs to, its sent and signed timestamps, totals and deposit terms. Archived quotes are left out and are not counted in total. Filter by creation date with created_after / created_before (both inclusive). Pages are limited to 100 rows and to an offset of 10,000 rows; beyond that, or when a page would read more than the per-call budget, the call answers 400 with a { "status": "error", "message": "..." } body and you should narrow the date window. total is exact up to 4,000 matching rows and a lower bound past that; has_more is authoritative.

Response shape: direct (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
page
integer >= 1
Default: 1

1-based page number.

limit
integer [ 1 .. 100 ]
Default: 100

Rows per page. A value above 100 is clamped to 100.

created_after
string or null <date-time>

Only quotes created at or after this instant. A full ISO 8601 datetime with timezone (for example 2026-09-01T00:00:00Z); a date alone is refused with 400. null means no bound.

created_before
string or null <date-time>

Only quotes created at or before this instant. Same format as created_after; must not be earlier than it.

Responses

Request samples

Content type
application/json
{
  • "page": 2,
  • "limit": 25,
  • "created_before": "2026-10-01T00:00:00-07:00"
}

Response samples

Content type
application/json
{
  • "page": 2,
  • "limit": 25,
  • "total": 61,
  • "has_more": true,
  • "results": [
    ]
}

Orders

List orders

Pages through the company's orders (purchase and work orders), newest first, with the project, quote, job and vendor each belongs to and its signing status and dates. Filter by creation date with created_after / created_before (both inclusive). Pages are limited to 100 rows and to an offset of 10,000 rows; beyond that, or when a page would read more than the per-call budget, the call answers 400 with a { "status": "error", "message": "..." } body and you should narrow the date window. total is exact up to 4,000 matching rows and a lower bound past that; has_more is authoritative. order_notes is returned as plain text.

Response shape: direct (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
page
integer >= 1
Default: 1

1-based page number.

limit
integer [ 1 .. 100 ]
Default: 100

Rows per page. A value above 100 is clamped to 100.

created_after
string or null <date-time>

Only orders created at or after this instant. A full ISO 8601 datetime with timezone (for example 2026-09-01T00:00:00Z); a date alone is refused with 400. null means no bound.

created_before
string or null <date-time>

Only orders created at or before this instant. Same format as created_after; must not be earlier than it.

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "page": 1,
  • "limit": 100,
  • "total": 1,
  • "has_more": false,
  • "results": [
    ]
}

Invoices

List invoices

Pages through the company's invoices, newest first, with totals, balance, status dates and the billing, project and rep details captured on each. Filter by creation date with created_after / created_before (both inclusive). Pages are limited to 100 rows and to an offset of 10,000 rows; beyond that, or when a page would read more than the per-call budget, the call answers 400 with a { "status": "error", "message": "..." } body and you should narrow the date window. total is exact up to 4,000 matching rows and a lower bound past that; has_more is authoritative.

Response shape: direct (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
page
integer >= 1
Default: 1

1-based page number.

limit
integer [ 1 .. 100 ]
Default: 100

Rows per page. A value above 100 is clamped to 100.

created_after
string or null <date-time>

Only invoices created at or after this instant. A full ISO 8601 datetime with timezone (for example 2026-09-01T00:00:00Z); a date alone is refused with 400. null means no bound.

created_before
string or null <date-time>

Only invoices created at or before this instant. Same format as created_after; must not be earlier than it.

Responses

Request samples

Content type
application/json
{
  • "created_after": "2026-09-01T00:00:00Z",
  • "created_before": "2026-09-30T23:59:59Z"
}

Response samples

Content type
application/json
{
  • "page": 1,
  • "limit": 100,
  • "total": 2,
  • "has_more": false,
  • "results": [
    ]
}

Payments

List payments

Pages through the company's payments, newest first, with the project and invoice each applies to, its status, method and amounts. Filter by creation date with created_after / created_before (both inclusive). Pages are limited to 100 rows and to an offset of 10,000 rows; beyond that, or when a page would read more than the per-call budget, the call answers 400 with a { "status": "error", "message": "..." } body and you should narrow the date window. total is exact up to 4,000 matching rows and a lower bound past that; has_more is authoritative.

Response shape: direct (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
page
integer >= 1
Default: 1

1-based page number.

limit
integer [ 1 .. 100 ]
Default: 100

Rows per page. A value above 100 is clamped to 100.

created_after
string or null <date-time>

Only payments created at or after this instant. A full ISO 8601 datetime with timezone (for example 2026-09-01T00:00:00Z); a date alone is refused with 400. null means no bound.

created_before
string or null <date-time>

Only payments created at or before this instant. Same format as created_after; must not be earlier than it.

Responses

Request samples

Content type
application/json
{
  • "limit": 10
}

Response samples

Content type
application/json
{
  • "page": 1,
  • "limit": 10,
  • "total": 1,
  • "has_more": false,
  • "results": [
    ]
}

Events

Check team member availability

Returns the open appointment start times for one or more team members between start_date and end_date, one row per team member. Start times come back as a single comma-separated string in the time zone of the company's default location, with no UTC offset. The window may span at most 30 days.

The dates are read as UTC when they carry no offset. When user_id, start_date or end_date is missing, body is [{ "start_times": "" }] rather than an error. A window over 30 days, or a calendar that could not be read, answers a { "status": "...", "error": true } object in body; an unknown team member, event type or date format answers the backend validation error in body. When the company's automations are paused, body is { "ok": true }.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
user_id
required
Array of strings or string

One team member id, or an array of them. Each is echoed back as sent. An unknown id answers the validation error.

start_date
required
string

Start of the window; ISO 8601, UTC when no offset is given.

end_date
required
string

End of the window; at most 30 days after start_date.

event_type
string

Event type id (see /v1/list/event_types); its duration and scheduling rules shape the slots. Optional.

Responses

Request samples

Content type
application/json
{
  • "user_id": "j97b4d2f8kq1nc5v7h3xm6tw9se0a2rz",
  • "start_date": "2026-10-15T00:00:00Z",
  • "end_date": "2026-10-17T00:00:00Z",
  • "event_type": "et4c8h2k6sq9nb3v1d7xm5wr0te4a8yc"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": [
    ]
}

Create or update an event

Reschedules an existing event, or creates one on a project. When event_id names a live event it is updated; otherwise a new event is created, which requires project_id.

start_date is required on every call. The end time is derived from the event type's default duration. The event type is event_type, else the existing event's type, else the company's first event type; the calendar is the existing event's, else the type's default, else the company's first calendar. The event title is always set to the event type name.

Omitted or blank fields are left unchanged, so a value cannot be cleared through this endpoint. An assignee that does not resolve is dropped and the existing assignee kept.

Side effects: a new event gets the project's main-contact email as a participant; event webhooks fire. A missing or unparseable start_date, or a company with no event types, answers the backend validation error in body. Creating without project_id, or with no calendar available, answers a { "status": "...", "error": true } object in body. When the company's automations are paused, body is { "ok": true } and nothing is written.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
start_date
required
string

New start. A string with a UTC offset is an absolute instant; one without is wall-clock time in time_zone, or UTC when none is given.

time_zone
string

ProLine time zone value (central, mountain, pacific, eastern, hawaii, arizona, atlantic, ...), a legacy option key such as central_standard_time, or an IANA name, for a start_date without offset.

event_id
string

Event to update; a legacy id is also accepted. Cancelled events do not match.

project_id
string

Project for a new event; a legacy id is also accepted. Required when creating.

event_type
string

Event type id (see /v1/list/event_types); sets the duration.

assignee
string

Team member id.

external_id
string

Your own id for the event, stored on it.

external_reschedule_link
string

A URL stored on the event for rescheduling it in your system.

skip_external_create
string

true, 1, yes or on: exclude the event from external calendar sync. The event is still created.

Responses

Request samples

Content type
application/json
{
  • "project_id": "p12d8f6h4tq0nb2v5k9xm3wr7se1c4ya",
  • "start_date": "2026-10-15T14:00:00",
  • "time_zone": "central",
  • "event_type": "et4c8h2k6sq9nb3v1d7xm5wr0te4a8yc",
  • "assignee": "j97b4d2f8kq1nc5v7h3xm6tw9se0a2rz",
  • "external_id": "cal-88213"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

Cancel an event

Cancels the event named by event_id. The event is marked cancelled, not deleted; /v1/find/event still returns it by event_id or external_id, with cancelled: true. Cancelling an already cancelled event runs the cancellation again and answers success. An event that came from an external calendar cannot be cancelled here; the call fails inside body.

A missing or unknown event_id answers { "status": "No existing event found.", "error": true } in body. When the company's automations are paused, body is { "ok": true } and nothing is written.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
event_id
required
string (ProLineId)

A ProLine record id. Opaque; in requests, a legacy id for the same record is also accepted.

Responses

Request samples

Content type
application/json
{
  • "event_id": "ev9f1j5m8xq3nb6v2d0km4wr7te1c9zd"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

Find an event

Looks up one event and returns the first match. The keys are tried in this order:

  1. event_id: exact lookup; a cancelled event is returned, with cancelled: true.
  2. external_id: exact lookup; a cancelled event is returned.
  3. project_id: the first live event on that project, within the day of start_date when given, otherwise within the next 365 days. event_type and assignee narrow the search.
  4. start_date alone: the first live event on that day, with the same filters, and only for days from today on.

Exact lookups ignore the filters. Date searches skip cancelled events. start_date on the two date searches is read as a plain date; the day window is taken in the server's clock, so an event near midnight may fall on the neighbouring day.

body is an array with one event, or an empty array when nothing matched (there is no not-found error). A project_id, event_type or assignee that does not resolve answers [].

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
event_id
string (ProLineId)

A ProLine record id. Opaque; in requests, a legacy id for the same record is also accepted.

external_id
string

The external_id stored on the event.

project_id
string

Project id; a legacy id is also accepted.

start_date
string

A date (YYYY-MM-DD, or a full date-time) selecting the day to search.

event_type
string

Event type id. Filter for the date searches.

assignee
string

Team member id. Filter for the date searches.

Responses

Request samples

Content type
application/json
{
  • "project_id": "p12d8f6h4tq0nb2v5k9xm3wr7se1c4ya",
  • "start_date": "2026-10-15"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": [
    ]
}

List event types

Returns every event type in the company, the default type first and the rest by name A to Z. There is no paging and no envelope. name and label carry the same value. The id is what event endpoints take as the event type.

Response shape: direct (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
object

Empty, or {}. Any key is refused with 400.

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
[
  • {
    },
  • {
    }
]

Activities

List activities

Pages through the activity feed of one project, contact or organization, newest first, optionally restricted to a set of activity types. Exactly one of project_id, contact_id or org_id is required. A parent that does not exist in the company gives an empty page with total: 0, not an error. Filter by creation date with created_after / created_before (both inclusive).

Each call may read at most about 12,000 activity records or 10 MB; when a page cannot be served within that, or the offset is past 10,000 rows, the call answers 400 with a { "status": "error", "message": "..." } body. For a project or organization the whole feed is read and the date window applied afterwards, so on a very busy project reduce limit as well as narrowing the window. total is exact up to 4,000 matching rows and a lower bound past that; has_more is authoritative.

Call rows omit the transcript segments; /v1/find/activity returns them. Ids on activities of migrated companies may be legacy ids (see Overview → Identifiers).

Response shape: direct (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
required
project_id
string (ProLineId)

A ProLine record id. Opaque; in requests, a legacy id for the same record is also accepted.

contact_id
string (ProLineId)

A ProLine record id. Opaque; in requests, a legacy id for the same record is also accepted.

org_id
string (ProLineId)

A ProLine record id. Opaque; in requests, a legacy id for the same record is also accepted.

type
Array of strings

Activity type names to include, for example call_created, note_created, email_sent, message_received, stage_change, quote_signed. Omitted or empty means every type. A name that is not an activity type is refused with 400 (Invalid activity type).

page
integer >= 1
Default: 1

1-based page number.

limit
integer [ 1 .. 100 ]
Default: 100

Rows per page. A value above 100 is clamped to 100.

created_after
string or null <date-time>

Only activities created at or after this instant. A full ISO 8601 datetime with timezone (for example 2026-09-01T00:00:00Z); a date alone is refused with 400. null means no bound.

created_before
string or null <date-time>

Only activities created at or before this instant. Same format as created_after; must not be earlier than it.

Responses

Request samples

Content type
application/json
{
  • "project_id": "k57abc1n0cq8vb3d5t6hm4ws2e7a9zq1",
  • "type": [
    ],
  • "limit": 20
}

Response samples

Content type
application/json
{
  • "page": 1,
  • "limit": 20,
  • "total": 7,
  • "has_more": false,
  • "results": [
    ]
}

Find an activity

Returns one activity by id, in the same shape as a /v1/list/activities row. For a call activity the transcript segments are included under call.transcript. The id may be a ProLine id or a legacy id.

When no activity with that id is visible to the company the call still answers 200, with an empty array [] as the body, not 404. An activity whose detail is larger than the response limit answers 400.

Response shape: direct (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
required
activity_id
required
string non-empty

The activity's id (ProLine or legacy). Surrounding whitespace is trimmed.

Responses

Request samples

Content type
application/json
{
  • "activity_id": "1718301234567x123456789012345678"
}

Response samples

Content type
application/json
Example
{
  • "activity_id": "1718301234567x123456789012345678",
  • "type": "call_created",
  • "created_at": "2026-09-11T14:02:00.000Z",
  • "title": "Outbound call",
  • "description": null,
  • "notes": null,
  • "project_id": "1718300000000x111111111111111111",
  • "project_ids": [
    ],
  • "org_ids": [ ],
  • "contact_id": "1718300000000x222222222222222222",
  • "created_by": {
    },
  • "call": {
    }
}

Create an alert activity

Creates an "alert" activity, titled alert_text, on a contact and/or a project. At least one of contact_id or project_id is required. One activity is created per project involved: the project you name plus the project linked to the contact, so a contact with a linked project yields two. A contact with no project gets a single contact-only activity. Nothing is deduplicated.

The answer's activity_created is the number of activities created, as a string; it is not an id (the call and message endpoints return an id under the same key).

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
alert_text
required
string

Activity title. Required by the backend.

alert_extended
string

Activity description.

contact_id
string

Contact to alert on, by ProLine or legacy id. One of contact_id or project_id is required.

project_id
string

Project to alert on, by ProLine or legacy id. One of contact_id or project_id is required.

Responses

Request samples

Content type
application/json
{
  • "contact_id": "j97f2x1n0cq8vb3d5t6hm4ws2e7a9zq1",
  • "alert_text": "Insurance adjuster scheduled",
  • "alert_extended": "Adjuster visit Tuesday 10am"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

Create a call activity

Logs a phone call with a contact: a call record is stored and a call activity is created from it. contact and type are required by the backend. The call is outbound when type is "outgoing" and inbound for any other value; a voicemail URL marks it as a voicemail.

Dedupe is by external_id: when a call with that external id already exists, nothing is created and body is { "error": "A call already exists with that external ID." }. An unknown contact answers the same way with "No contact could be found for that ID." Without external_id, repeated sends log the call again.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
contact
required
string

The contact, by ProLine or legacy id. Required by the backend.

type
required
string

"outgoing" for an outbound call; any other value is logged as inbound. Required by the backend.

user
string

The team member on the call, by ProLine or legacy id. An unknown user is ignored.

duration
string

Call length in seconds, as a numeric string.

recording
string <uri>

URL of the call recording.

voicemail
string <uri>

URL of the voicemail. When present the call is logged as a voicemail.

external_id
string

Your id for the call. Dedupe key; a repeat is refused.

call_note
string

Note on the call.

Responses

Request samples

Content type
application/json
{
  • "contact": "j97f2x1n0cq8vb3d5t6hm4ws2e7a9zq1",
  • "type": "incoming",
  • "duration": "312",
  • "external_id": "ringcentral-88211",
  • "call_note": "Asked for a Saturday inspection"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

Create a message activity

Logs a text message exchanged with a contact: a message record is stored, attached to the contact's latest project, and a message-sent or message-received activity is created from it. contact, type and message_body are required by the backend. The message is outbound when type is "outgoing" and inbound for any other value.

Dedupe is by external_id: when a message with that external id already exists, nothing is created and body is { "error": "A message already exists with that external ID." }. An unknown contact answers the same way with "No contact could be found for that ID." Without external_id, repeated sends log the message again.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
contact
required
string

The contact, by ProLine or legacy id. Required by the backend.

type
required
string

"outgoing" for a message the company sent; any other value is logged as received. Required by the backend.

message_body
required
string

The message text. Required by the backend.

message_note
string

Note on the message.

user
string

The team member who sent or received it, by ProLine or legacy id.

external_id
string

Your id for the message. Dedupe key; a repeat is refused.

Responses

Request samples

Content type
application/json
{
  • "contact": "j97f2x1n0cq8vb3d5t6hm4ws2e7a9zq1",
  • "type": "outgoing",
  • "message_body": "Your crew arrives Thursday at 8am.",
  • "external_id": "twilio-SM9f3a1c"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

Files

List project files

Pages through the files of one project, newest first, with each file's download URL, folder and photo details. project_id is required. Restrict the listing to one folder with folder_id (null for the files in no folder), to a media type with mime_type_prefix (for example image/), and to a creation window with created_after / created_before (both inclusive). Archived folders and the files in them are left out. A project or folder that does not exist in the company gives an empty page, not an error.

A project with more than 2,000 folders cannot be listed whole: the call answers 400 and you pass folder_id to list one folder at a time. Pages are limited to 100 rows and to an offset of 10,000 rows; beyond that, or when a page would read more than the per-call budget, the call answers 400 with a { "status": "error", "message": "..." } body. total is exact up to 4,000 matching rows and a lower bound past that; has_more is authoritative.

Response shape: direct (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
required
project_id
required
string (ProLineId)

A ProLine record id. Opaque; in requests, a legacy id for the same record is also accepted.

folder_id
string or null non-empty

Only files directly in this folder. null means files in no folder. Omitted means every folder.

mime_type_prefix
string or null non-empty

Only files whose media type starts with this prefix, for example image/ or application/pdf. null is the same as omitting it. A blank string is refused with 400.

page
integer >= 1
Default: 1

1-based page number.

limit
integer [ 1 .. 100 ]
Default: 100

Rows per page. A value above 100 is clamped to 100.

created_after
string or null <date-time>

Only files created at or after this instant. A full ISO 8601 datetime with timezone (for example 2026-09-01T00:00:00Z); a date alone is refused with 400. null means no bound.

created_before
string or null <date-time>

Only files created at or before this instant. Same format as created_after; must not be earlier than it.

Responses

Request samples

Content type
application/json
{
  • "project_id": "k57abc1n0cq8vb3d5t6hm4ws2e7a9zq1",
  • "folder_id": null,
  • "mime_type_prefix": "image/",
  • "limit": 50
}

Response samples

Content type
application/json
{}

List project folders

Pages through the folders of one project. project_id is required. With parent_folder_id the direct children of that folder are returned (null for the top level); without it every live folder is returned in tree order, each folder followed by its descendants. Rows are in tree order, not newest first. Archived folders are left out. A project or folder that does not exist in the company gives an empty page, not an error.

This endpoint takes no date filters: created_after and created_before are refused with 400 like any other unknown key. A project with more than 2,000 folders cannot be listed whole: the call answers 400 and you pass parent_folder_id to list one level at a time. Pages are limited to 100 rows and to an offset of 10,000 rows. total is exact up to 4,000 matching rows and a lower bound past that; has_more is authoritative.

Response shape: direct (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
required
project_id
required
string (ProLineId)

A ProLine record id. Opaque; in requests, a legacy id for the same record is also accepted.

parent_folder_id
string or null non-empty

Only the direct children of this folder. null means the top-level folders. Omitted means every folder, in tree order. A blank string is refused with 400.

page
integer >= 1
Default: 1

1-based page number.

limit
integer [ 1 .. 100 ]
Default: 100

Rows per page. A value above 100 is clamped to 100.

Responses

Request samples

Content type
application/json
{
  • "project_id": "k57abc1n0cq8vb3d5t6hm4ws2e7a9zq1",
  • "parent_folder_id": null
}

Response samples

Content type
application/json
{
  • "page": 1,
  • "limit": 100,
  • "total": 3,
  • "has_more": false,
  • "results": [
    ]
}

Attach a file to a project

Downloads the file at file_url and attaches it to the project named by project_id. It is the same storage path as /v1/import/file, but the project is given by id only; use the import endpoint to target a project by external id or through a contact. Nothing is deduplicated: sending the same URL twice stores two files.

project_id and file_url are required by the backend. The URL is probed before download; the file may be at most 50,000,000 bytes (50 MB). When the probe fails or reports a larger size, body is an error object with statusCode: 400 and a message beginning "Temporary error connecting to CloudFlare Functions - Get File Size", rather than the usual outcome shape. The URL must be publicly reachable.

This endpoint needs the import permission on your partner key, as the /v1/import/* paths do, not a general one.

Values are sent as strings; arrays are flattened to comma-separated text.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
project_id
required
string

Target project, by ProLine or legacy id. Required by the backend.

file_url
required
string <uri>

Public URL to download. Required by the backend.

file_name
string

Name to store the file under, truncated to 100 characters. Defaults to the file name in the URL.

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

Import

Import a project

Creates one project from an external system, and its main contact when no existing contact matches. The call is idempotent on project_external_id: when a project with that external id (or the project named by proline_id) already exists, nothing is created and the answer carries duplicate: true with the existing ids. The duplicate message always names the external id, even when proline_id matched.

The contact is found by contact_id, then contact_external_id, then contact_email, then contact_phone. When none matches, a contact is created from the contact_* fields; at least one of first name, last name, email or phone must be supplied, or the import fails. Lookup values such as stage, lead_source and assignee take a ProLine id, a legacy id or the record's name; an unresolved value is skipped, except stage, which falls back to the first stage of the company's project pipeline.

Side effects: a created contact gets its default folders, field-change activities and the contact-created webhook; the project is queued for accounting sync, address geocoding and stage workflows.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
project_external_id
string

Your id for the project. Idempotency key; a repeat is skipped with duplicate: true.

proline_id
string

An existing project's ProLine or legacy id. When found, the call is skipped as a duplicate.

contact_id
string

Existing contact to attach, by ProLine or legacy id.

contact_external_id
string

Existing contact to attach, by the external id given at contact import.

contact_email
string

Finds an existing contact by email; otherwise the email of the created contact.

contact_phone
string

Finds an existing contact by phone; otherwise the phone of the created contact.

contact_first_name
string

First name of the contact to create.

contact_last_name
string

Last name of the contact to create.

contact_type
string

Contact type, by ProLine id, legacy id or name. Unresolved values are skipped.

lead_source
string

Lead source, by ProLine id, legacy id or name. Unresolved values are skipped.

string or Array of strings

Tags for the contact, as an array or a comma-separated string; trimmed and deduplicated. Unknown tags are skipped, not created.

project_name
string

Project name. Defaults to a name built from the contact.

project_number
string

Project number. When given it is used as-is and the company's counter moves past it; otherwise the next number is allocated.

stage
string

Project stage, by ProLine id, legacy id or name. Unresolved values fall back to the company's first project stage; a company with no project stage fails the import.

location
string

Location, by ProLine id, legacy id or name. Unresolved values are skipped.

area
string

Area, by ProLine id, legacy id or name. Unresolved values are skipped.

assignee
string

Assigned team member, by ProLine id, legacy id or name. Unresolved values are skipped.

category
string

Project category, by ProLine id, legacy id or name. Unresolved values are skipped.

type
string

Project type, by ProLine id, legacy id or name. Unresolved values are skipped.

string or Array of strings

Services, as an array or a comma-separated string of ids, legacy ids or names.

string or Array of strings

Project tags, as an array or a comma-separated string. Unknown tags are skipped, not created.

address1
string
address2
string
city
string
state
string
zip
string
notes
string

Project notes. When cost is sent, "Imported cost: …" is appended.

custom_field_1
string

Written to the company's first legacy custom-field slot. Companies without legacy slots skip it.

custom_field_2
string

As custom_field_1, second slot.

custom_field_3
string

As custom_field_1, third slot.

custom_field_4
string

As custom_field_1, fourth slot.

custom_field_5
string

As custom_field_1, fifth slot.

custom_field_6
string

As custom_field_1, sixth slot.

custom_field_7
string

As custom_field_1, seventh slot.

revenue
string

Expected revenue as a numeric string. Non-numeric values are skipped.

cost
string

Not stored as a budget; appended to notes as "Imported cost: …".

lead_date
string

Date the project became a lead. ISO 8601 or MM/DD/YYYY; a value without an offset is read in the company's time zone. Unparseable values are skipped.

inspection_date
string

As lead_date.

open_date
string

As lead_date.

won_date
string

As lead_date.

completed_date
string

As lead_date.

closed_date
string

As lead_date.

disqualified_date
string

As lead_date.

lost_date
string

As lead_date.

Responses

Request samples

Content type
application/json
{
  • "project_external_id": "crm-7781",
  • "contact_first_name": "Dana",
  • "contact_last_name": "Whitfield",
  • "contact_email": "dana.w@example.com",
  • "contact_phone": "(555) 010-2244",
  • "project_name": "Whitfield re-roof",
  • "stage": "New Lead",
  • "lead_source": "Angi",
  • "address1": "14 Juniper Ct",
  • "city": "Boise",
  • "state": "ID",
  • "zip": "83702",
  • "services": [
    ],
  • "revenue": "18500",
  • "lead_date": "2026-09-30"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

Import a contact

Creates one contact from an external system. The call is idempotent on external_id only: when a contact with that external id already exists, nothing is created and the answer carries duplicate: true with the existing ids. There is no matching by email or phone, so repeated sends without external_id create duplicate contacts.

At least one of first_name, last_name, email or phone is required, or the import fails. The phone is normalised to E.164 (digits only as a fallback) and dropped when too short. Lookup values (assigned, contact_type, lead_source, organization, contact_tags) take a ProLine id, a legacy id or the record's name; an unresolved value is skipped, never created.

Side effects: the contact gets its default folders, field-change activities and the contact-created webhook.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
external_id
string

Your id for the contact. Idempotency key; a repeat is skipped with duplicate: true.

first_name
string
last_name
string
email
string
phone
string

Normalised to E.164; dropped when too short to be a phone number.

organization
string

Organization, by ProLine id, legacy id or name. Unresolved values are skipped.

address1
string
address2
string
city
string
state
string
zip
string
assigned
string

Assigned team member, by ProLine id, legacy id or name. Unresolved values are skipped.

contact_type
string

Contact type, by ProLine id, legacy id or name. Unresolved values are skipped.

lead_source
string

Lead source, by ProLine id, legacy id or name. Unresolved values are skipped.

string or Array of strings

Tags, as an array or a comma-separated string; trimmed and deduplicated. Unknown tags are skipped, not created.

notes
string
custom_field_1
string

Written to the company's first legacy custom-field slot. Companies without legacy slots skip it.

custom_field_2
string

As custom_field_1, second slot.

custom_field_3
string

As custom_field_1, third slot.

Responses

Request samples

Content type
application/json
{
  • "external_id": "hs-20931",
  • "first_name": "Marcus",
  • "last_name": "Oyelaran",
  • "email": "marcus@example.net",
  • "phone": "5550198877",
  • "contact_type": "Homeowner",
  • "lead_source": "Website",
  • "contact_tags": "referral,spring-promo",
  • "city": "Tulsa",
  • "state": "OK"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

Import an activity

Logs a historical activity (a call, message, note or email) against a contact and/or a project. One activity record is created; no call or message record is, unlike /v1/activity/create_call and /v1/activity/create_message. Nothing is deduplicated: sending the same activity twice logs it twice.

activity_type and date must be present or the request is refused with 400 before it reaches the backend. At least one of contact_id, contact_external_id, project_id or project_external_id must resolve, or the import fails. The activity's title is subject, else note, else "Imported "; its description is body, else note when a subject was given.

Values are sent as strings; arrays are flattened to comma-separated text.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
required
activity_type
required
string

Case-insensitive. call, message, text or sms (both logged as a message), note or email. Any other value is logged as a note; the value you sent is kept on the activity.

date
required
string

When the activity happened. ISO 8601 or MM/DD/YYYY; a value without an offset is read in the company's time zone. An unparseable value falls back to now.

subject
string

Activity title.

body
string

Activity description.

note
string

Fallback for the title when subject is absent, or for the description when subject is present and body is absent.

user
string

The team member who performed the activity, by ProLine id, legacy id or name.

voicemail
string

Voicemail URL. Stored on the activity only; no call record is created.

recording
string

Recording URL. Stored on the activity only.

duration
string

Duration in seconds, as a string. Stored on the activity only.

contact_id
string

Contact to log against, by ProLine or legacy id.

contact_external_id
string

Contact to log against, by the external id given at contact import.

project_id
string

Project to log against, by ProLine or legacy id.

project_external_id
string

Project to log against, by the external id given at project import.

Responses

Request samples

Content type
application/json
{
  • "activity_type": "call",
  • "date": "2026-09-28T15:30:00-06:00",
  • "contact_external_id": "hs-20931",
  • "subject": "Follow-up call",
  • "body": "Discussed shingle colors",
  • "duration": "420",
  • "user": "sales@example.com"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

Import a project file

Downloads the file at file_url and attaches it to a project. The project is found by project_id or project_external_id; failing those, by contact_id or contact_external_id, in which case the contact's newest project is used. A contact with no project fails the import.

file_name and file_url must be present, along with at least one of the four reference keys, or the request is refused with 400 before it reaches the backend. The URL must be publicly reachable. The file may be at most 50,000,000 bytes (50 MB); larger files fail the import. Nothing is deduplicated: sending the same URL twice stores two files.

Values are sent as strings; arrays are flattened to comma-separated text.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
required
file_name
required
string

Name to store the file under, truncated to 100 characters. The key must be present; a blank value falls back to the file name in the URL.

file_url
required
string <uri>

Public URL to download. A blank value fails the import.

project_id
string

Target project, by ProLine or legacy id.

project_external_id
string

Target project, by the external id given at project import.

contact_id
string

Contact whose newest project is the target, by ProLine or legacy id. Used only when no project key is given.

contact_external_id
string

Contact whose newest project is the target, by the external id given at contact import. Used only when no project key is given.

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

Import taxonomy values

Creates taxonomy values (contact types, tags, lead sources, project types, categories, services and areas) by name, so that later imports can refer to them by name. Each key takes an array or a comma-separated string of names. A name that already exists in the company (or matches a legacy id) is left alone and listed under ignored; the others are created and listed under created. {} is accepted and answers with empty created and ignored objects.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
string or Array of strings (NameList)
string or Array of strings (NameList)
string or Array of strings (NameList)
string or Array of strings (NameList)
string or Array of strings (NameList)
string or Array of strings (NameList)
string or Array of strings (NameList)
string or Array of strings (NameList)

Responses

Request samples

Content type
application/json
{
  • "lead_sources": [
    ],
  • "project_services": "Roof Replacement, Gutters"
}

Response samples

Content type
application/json
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": {
    }
}

General

Find a team member

Looks up one team member of the company by ProLine phone number, email or display name. The strategies are tried in that order and the first match wins: proline_number is normalised to E.164 and matched against the company's ProLine phone lines; user_email is an exact, case-sensitive match; display_name is matched on the text before its first comma, trimmed and ignoring case.

Administrators of the ProLine platform and limited-access users are never returned. The answer is an array with at most one element; no match, or an empty body, gives [].

Values are sent as strings; arrays are flattened to comma-separated text.

Response shape: wrapped (see Overview → Response shapes).

Authorizations:
(PartnerKeyCompanyKey)
Request Body schema: application/json
optional
proline_number
string

A ProLine phone line assigned to the member, in any common format.

user_email
string

The member's login email, matched exactly (case-sensitive).

display_name
string

The member's display name. Only the text before the first comma is compared, ignoring case and surrounding spaces.

Responses

Request samples

Content type
application/json
{
  • "user_email": "jess.park@example-roofing.com"
}

Response samples

Content type
application/json
Example
{
  • "isBase64Encoded": false,
  • "statusCode": 200,
  • "statusDescription": "200 OK",
  • "headers": {
    },
  • "body": [
    ]
}