TEC World API
Connect your own tools, reports and integrations to your TEC World workspace.
Overview
The TEC World API gives your own tools, reports and integrations access to your workspace's data over HTTPS. Requests and responses are JSON, every request is authenticated with an API key, and a key can never see more than the person it belongs to can see in TEC World.
The API reads every module listed under Endpoints and can create, update, delete and restore the records whose endpoints list those verbs.
The machine-readable OpenAPI 3.1 description of everything on this page is at /developers/openapi.json. Import it into Postman, Insomnia or a client generator.
Use of the API is governed by your organization's Terms of Service.
Getting a key
A workspace admin creates keys in Settings > Integrations > Private API, for themselves or for a member. A member can also create their own in Settings > My API Keys.
- Give each key a name that says which integration uses it, so you can find and revoke it later.
- Turn on only the access groups the integration needs. The Scopes table under Reference says which group grants each scope. A scope marked as not offered on the key form cannot be added to a key there yet.
- Copy the secret when it is shown. It is shown once and cannot be retrieved later. If it is lost, revoke the key and create a new one.
- Keep keys out of browser code, public websites and mobile apps. Anyone holding a key can read what its owner can read in TEC World.
- The key settings page also shows your API address and a test request you can paste into a terminal.
- Each key in the list shows when TEC World last accepted it ("Last used 3 hours ago", or "Not used yet"), so you can tell whether an integration is calling. A request turned away before the key is let in, for example for a missing permission or too many requests, does not count. A key made before October 2, 2026 that has not been used since says "Not used since" that date, because uses were not recorded before then.
A key belongs to one person and acts as them: it sees what they see, and it stops working as soon as they are deactivated or lose the access it needs. A key on a person's account therefore breaks when that person leaves or changes role.
For an integration the whole company relies on, such as a Power BI refresh or a data warehouse sync, we recommend a dedicated account used only for integrations (for example "Integrations", with a shared mailbox your team monitors, so its alerts reach someone). Make it an admin so it can read every record of the access groups you give its key, and turn on only the groups the integration needs (read-only unless the integration changes records); every change made through its key is recorded under its name. Keep its sign-in details in your company's password manager rather than with one person, do not use it for everyday work in TEC World, and note when its key stops working (at most a year after it is made) so you can replace it in time. The account takes a seat like any other user. A key for your own spreadsheet or AI assistant belongs on your own account.
Authentication
Send the key as a bearer token on every request. Keys start with tcrm_.
curl "https://your-crm-address/api/v1" \
-H "Authorization: Bearer tcrm_your_key_here"GET /api/v1 checks a key: it answers the key's details and the endpoints the API serves. GET /api/v1/health adds the key's rate-limit settings and its recent request activity.
What a key can read is the overlap of its scopes and its owner's own access in TEC World: their role, their territory and manufacturer assignments, and the app's own-records rules. If the owner loses access to a module, the key loses it on the next request. A missing, wrong, expired or revoked key answers 401. A key without the scope an endpoint needs, or whose owner cannot read that module, answers 403.
Requests and responses
Every response is JSON with ok, and carries the same three values in its body and in its headers:
requestId, headerx-torch-request-id: quote it when you contact support about a request.apiVersion, headerx-torch-api-version: the response format version (see Versioning).deprecated, headerx-torch-api-deprecated:truewhen the version you are on is scheduled to change.
A list answers data (an array), pagination and sync. A single record answers data (an object).
{
"ok": true,
"requestId": "5f9b2dc7-c130-4bb4-b7f5-47d7f2d8a1cb",
"apiVersion": "2026-05-01",
"deprecated": false,
"data": [],
"pagination": { "page": 1, "pageSize": 50, "total": 0, "totalPages": 1, "hasNextPage": false, "hasPreviousPage": false },
"sync": { "fields": [], "includes": [], "updatedSince": null, "createdSince": null, "nextCursor": null }
}Ignore fields you do not recognize: new fields can be added to a response at any time without a version change. Any field other than id can be null or, where a field needs extra access, left out.
Long-text fields such as notes and activity bodies are always plain text with lines separated by a newline. Formatting applied in the app is removed, so you never receive HTML.
Paging and search
Every list endpoint takes these query parameters:
page: the page number, from1(the default).pageSize: records per page,50by default and at most200.search: a text search over the module's main fields, at most 200 characters.fields: a comma-separated list of the fields you want back, such asid,updated_at.idis always included, and a name the endpoint does not return is ignored.
curl "https://your-crm-address/api/v1/accounts?page=1&pageSize=10&search=acme" \
-H "Authorization: Bearer tcrm_your_key_here"Incremental sync
To keep a copy of your data up to date without re-reading everything, pull only what changed:
updatedSince: an ISO 8601 timestamp. Only records changed at or after it are returned, on the modules that track changes.createdSince: the same, for records created at or after it.cursor: each list answerssync.nextCursor. Send it back unchanged ascursorto get the next batch, and keep going untilsync.nextCursorisnull. A malformed cursor answers400.
A sync run that never misses a change:
- Note the time each run starts, and save it once the run has finished.
- Begin the next run with
updatedSincea few minutes before that start. The time is inclusive and the windows overlap, so a record can come back twice: update your copy byidrather than adding rows. - Follow
sync.nextCursoruntil it isnull, sending the same parameters with each cursor. A list that hands out no cursor answersnullon every page: page it withpageuntilpagination.hasNextPageisfalse. - Send timestamps in UTC ending in
Z, or encode+as%2B. A raw+arrives as a space, and a timestamp the API cannot read is ignored, so the whole list comes back. When you sent one, check thatsync.updatedSincein the answer is notnull. - Commission lines have no change time, so
updatedSincedoes not narrow them: read them in full on each run. - Ask only for the fields you keep, with
fields.
curl "https://your-crm-address/api/v1/contacts?updatedSince=2026-10-11T14:55:00Z&pageSize=200&fields=id,updated_at,email" \
-H "Authorization: Bearer tcrm_your_key_here"Changes never tell your copy that a record was deleted. Now and then (weekly suits most copies), read the whole list with fields=id, following the cursor, and drop the ids it no longer returns. That also drops records the key can no longer see.
Deleted records
A copy kept up to date with updatedSince never learns which records are gone. GET /api/v1/deletions?resource=<resource> lists the ids of one resource's deleted records, oldest first, so your copy can drop them. It takes the same key and read scope as that resource's list, and it only names a record the key would be allowed to read if the record still existed: the list's own visibility rules, applied to the record as it was stored.
resource(required): a resource name from the Endpoints reference, such asaccounts. An unknown name answers400.since: an ISO 8601 timestamp. Only deletions at or after it are returned. Send the time the previous run started minus 15 minutes.deletedAtis when a deletion began, and a large one can take a while to finish, so it can appear after a run that started later than itsdeletedAt. The overlap catches it. The same row can then come back, and dropping an id twice does no harm.pageSizeandcursorwork as on every list. A page shorter thanpageSizeis the last one, andsync.nextCursorisnull. A cursor belongs to theresourceandsinceit came from. It marks a position, not a permission: it is not tied to the key, and a page read with it still lists only rows that key may read. Followsync.nextCursorrather thanpagination.total, which is a full count only on the first page, for a key whose owner can read every record of the resource.- Each row has
id,resource,deletedAtandkind.kindissoftfor a record that can still be brought back by an admin for 30 days (if it is, it leaves this list), orhardfor a record that is gone for good. A hard deletion is listed when the key would have been allowed to read the record, judged on what the record, and the company, quote or sale it was linked to, held when it was deleted. When that can no longer be shown, the row is left out for that key: for example a hard deletion recorded before the API kept those values, a record linked to a company, quote or sale that was already in Recently Deleted on its own, or a commission line the Commissions report would have to match again. Hard deletions are kept for 12 months. A key whose owner can read every record of the resource gets every hard deletion. - Commissions: each recalculation of a month deletes its draft lines and creates them again under new ids, so every replaced draft is listed here as a
harddeletion, and one recalculation can list many. Drop the old id, and pick up the new line from the commissions list withupdatedSince. - Unlike
updatedSince, asincethe API cannot read answers400, so send UTC ending inZor encode+as%2B. - Read the list at least every 12 months: hard deletions are kept for 12 months, then they are gone. A
sinceolder than that still works, but some hard deletions may be missing, so every page then hassync.sinceBeforeJournalWindow: true(andsync.journalRetainedFrom, the oldest deletion still kept): read the whole list once to find what is gone. A deleted record can be removed for good 31 days after its deletion, and it then appears here ashard, but a key limited to some territories, manufacturers or its own records may not be shown every one. With such a key, read the list at least every 30 days, and now and then read the whole list withfields=idtoo, which catches those.
curl "https://your-crm-address/api/v1/deletions?resource=accounts&since=2026-10-01T00:00:00Z" \
-H "Authorization: Bearer tcrm_your_key_here"Filters and sorting
Some lists also take filters and a sort, listed under each resource below. filter[<field>]=<value> matches a value exactly, and filter[<field>][<op>]=<value> takes the operators the table lists: eq, in (comma-separated, up to 50 values), gte, lte or is_null (true for empty, false for has a value). For example filter[created_at][gte]=2026-01-01. Filters combine with each other and with search, and only ever narrow what the key can see.
sort=<field> sorts ascending and sort=-<field> descending, one field at a time. Dates are YYYY-MM-DD and timestamps ISO 8601. On a timestamp field, gte or lte with a date alone covers that whole day in UTC. A filter or sort the table does not list answers 400 naming the parameter. A cursor belongs to the query it came from: changing the filters, search or sort while following one answers 400, so start again without it.
Embedding related records
A few reads take include=<name> to embed related records on each record, saving a call per record: GET /api/v1/accounts and /api/v1/accounts/{id} take include=contacts (the company's contact links), GET /api/v1/contacts and /api/v1/contacts/{id} take include=accounts (the contact's company links), and GET /api/v1/quotes/{id} takes include=items (the quote's line items). Each embed is exactly what the matching endpoint lists for that record, under the same scope and visibility rules.
- The embed sits on the record under the include's name as
{ "data": [...], "has_more": false }.dataholds at most 100 rows, in that endpoint's order;has_moreistruewhen the record has more, and the endpoint itself pages the rest. - An include that needs another scope is refused outright:
include=contactsneedscontacts:readandinclude=accountsneedsaccounts:read(403 missing_scope), and the key's owner must be able to read that module (403 forbidden). A name the read does not take is400 invalid_request. - On a list,
pageSizeis at most50with an include (400past it), andsync.includeslists what was embedded. A single-record read with an include answerssync.includestoo. fieldsnarrows the record only, never the embed. An embed is asked for by name, so it is always present once asked for, with an emptydatawhen the record has nothing to embed.
curl "https://your-crm-address/api/v1/accounts?pageSize=50&include=contacts" \
-H "Authorization: Bearer tcrm_your_key_here"Creating and changing records
POST creates a record and PATCH changes the fields you send. A field a write does not accept, including one the server sets itself, answers 400 and nothing is saved. Each write answers the record as a read returns it.
- Idempotency: send an
Idempotency-Keyheader (any unique string, such as a UUID) on a create, a delete or a restore. Retrying with the same key and body answers the first result instead of acting twice. The same key with a different body answers409. Where an endpoint requires the header, a call without it answers428. A key is remembered for 7 days after the last call that ran with it (a retry answered from memory does not extend that). After that, the same key and body is a new request and acts again. - Concurrency: a
PATCHcan sendexpected_updated_at, theupdated_ata read returned. If the record changed since, the answer is409with the latestupdated_at, and nothing is saved. - Deletes are soft: for 30 days,
POST .../restorewith a key of the same person who deleted the record brings it back. After that the answer is410. - A write body is at most 64 KiB unless the endpoint says otherwise. A larger one answers
413.
Errors
An error answers ok: false, a human-readable error and a machine-readable code:
{
"ok": false,
"error": "Private API token is missing the required scope",
"code": "forbidden",
"requestId": "5f9b2dc7-c130-4bb4-b7f5-47d7f2d8a1cb",
"apiVersion": "2026-05-01",
"deprecated": false
}400invalid_request: a parameter or body is not valid. Theerrorsays which.401unauthorized: the key is missing, wrong, expired or revoked.402payment_required: the workspace has no active subscription. A workspace admin can subscribe in the app's billing settings; retrying will not help until then.403forbidden: the key lacks the scope, or its owner lacks the access.404not_found: no such record in your workspace, or one the key cannot see. The two look the same on purpose.409conflict: the record changed since you read it, or an idempotency key was reused with a different body.429rate_limited: too many requests (see Limits). When the answer carries aRetry-Afterheader, wait that many seconds before retrying.503service_unavailable: a required service is briefly unavailable. Retry later.500internal_error: something went wrong on our side. Retry later, and contact support with therequestIdif it persists.
Write endpoints can also answer more specific codes, such as 409 duplicate or paid_locked, 410 restore_expired, 413 payload_too_large, 422 constraint and 428 precondition_required. Each operation in the OpenAPI description lists the statuses it can answer.
Handle errors by HTTP status first, then use code for detail. Treat a code you do not recognize by its status, and never match on the wording of error, which can change.
Limits
- Each key may make 300 requests a minute, across every endpoint and every program using it. Beyond that the answer is
429. - All of one person's keys together may make 400 requests a minute, counted toward the person each key belongs to. Beyond that their requests answer
429until the minute ends; their colleagues' keys are not held to it. - All of a workspace's keys together may make 1,200 requests a minute, whichever keys and programs send them. Beyond that every key of the workspace gets
429until the minute ends, so a job spread over several keys should pace them together. - Reads also share a daily allowance with the AI assistant (MCP) server: 1,000 a day for everything one person's keys and connections do, and 10,000 a day for the whole workspace, unless your workspace has its own numbers.
GET /api/v1andGET /api/v1/healthdo not count. Past it the answer is429with aRetry-Afterheader: the seconds until the allowance resets. - Failed sign-ins are throttled separately: after 20 failures in 5 minutes from one address with the same key, further failures answer
429instead of401until the 5 minutes pass. A correct key keeps working. - Creating, changing, deleting and restoring records have lower limits of their own per key, which also answer
429with aRetry-Afterheader. - On a
429, wait the number of seconds in theRetry-Afterheader when the answer has one; otherwise wait up to a minute. Then send the same request again. GET /api/v1/healthshows a key's current limits (its own, its owner's and its workspace's, the daily allowance included) and its recent failures.
Versioning
The API's paths start with /api/v1, and every response names its format version in apiVersion (currently 2026-05-01). There is one version today, and a request cannot ask for another. Adding an endpoint, a field, a filter, an error code or an event is not a breaking change and can happen without notice, so build your client to tolerate them. When a version is scheduled to be retired, its responses will answer deprecated: true before it changes: watch for that value.
Webhooks
A webhook sends an event to an HTTPS address you run each time something it subscribes to happens, so you do not have to poll for it. A workspace admin adds one on the Connections page of the app's integration settings, choosing which events it sends, and copies its signing secret, which is shown once.
Each event arrives as an HTTPS POST request with a JSON body and these headers:
x-torch-event: the event name, such ascontact.created.x-torch-delivery: this delivery's id, the same on every retry of it.x-torch-timestamp: when this attempt was sent, in ISO 8601 UTC. Every attempt has a new one.x-torch-signature:sha256=followed by the signature in lowercase hex. For 24 hours after the webhook's signing secret is rotated it carries two, separated by a comma: the first made with the new secret, the second with the old one.
{
"eventId": "6f1c0d9e2b7a4c3d8e5f1a2b",
"eventType": "contact.created",
"entityType": "contact",
"entityId": "3f6c2a1e-8b4d-4c2a-9e7f-1d2b3c4d5e6f",
"actorUserId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
"workspaceId": "b2a1c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"occurredAt": "2026-10-11T15:04:05.123Z",
"data": { "id": "3f6c2a1e-8b4d-4c2a-9e7f-1d2b3c4d5e6f", "name": "Grace Hopper", "email": "grace@example.com" }
}eventId names the event: every delivery and retry of it carries the same one. entityType and entityId name the record, actorUserId the person whose action caused it (or null), and occurredAt when the change happened, which can be well before the event reaches you. data holds ids and a few labels, never the whole record: read the record through the API for its current state.
To check that an event is genuine, compute an HMAC-SHA256 keyed with the webhook's secret over the timestamp header, a period and the raw request body exactly as received, and compare it with each sha256= entry of the signature header using a constant-time comparison: the event is genuine when any entry matches. Refuse an event whose timestamp is more than 5 minutes from your clock. Each retry is signed again with a fresh timestamp, so a genuine retry is never refused. When a workspace admin rotates the signing secret, events are signed with both the new and the old secret for 24 hours: put the new secret in place within that time.
import crypto from "node:crypto";
// rawBody: the request body exactly as received (a Buffer); headers: the request's headers.
function verifyEvent(rawBody, headers, secret) {
const timestamp = headers["x-torch-timestamp"] ?? "";
const sentAt = Date.parse(timestamp);
if (!Number.isFinite(sentAt) || Math.abs(Date.now() - sentAt) > 5 * 60 * 1000) return null;
const wanted = Buffer.from("sha256=" + crypto.createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest("hex"));
// One entry, or two (new, old) for 24 hours after a rotation: any match is genuine.
const entries = String(headers["x-torch-signature"] ?? "").split(",").map((entry) => Buffer.from(entry.trim()));
if (!entries.some((given) => given.length === wanted.length && crypto.timingSafeEqual(given, wanted))) return null;
return JSON.parse(rawBody.toString("utf8"));
}- Answer with any
2xxstatus within 15 seconds, and do slow work after answering. Only a2xxcounts as delivered: any other status, a redirect (never followed), a timeout or a failed connection is retried. - Retries come no sooner than 5, 10, 15 and 30 minutes, then 1, 2, 4 and 8 hours, then 14 hours four times after each failed attempt, and often later: about 3 days in all. After the 13th failed attempt the event is not retried to that address.
- Delivery is at least once: the same event can arrive more than once, so skip an
eventIdyou have already handled. - Order is not guaranteed: a retry can arrive after newer events. Use
occurredAtwhen order matters.
Events are sent when most records are created, updated or deleted, through the API or in the app. On the Connections page a webhook's events are chosen in groups, such as Companies or Quotes, or as Everything, which also takes kinds of event added later. Imports and merges send none, so keep a copy current with updatedSince rather than with events alone.
Reference
The scopes a key can hold, then each resource with its endpoints, the scope they need and the fields it returns. In the endpoint paths, {id} is a record's id. "Returned by" says whether a field comes back in the list, the single-record read, or both. A resource without a field table returns the fields its OpenAPI description lists.
Scopes
| Scope | Allows | Access group on the key form |
|---|---|---|
accounts:read | Read | Customers & Contacts |
contacts:read | Read | Customers & Contacts |
quotes:read | Read | Quotes & Sales |
activities:read | Read | Tasks & Activity |
manufacturers:read | Read | Products & Inventory |
products:read | Read | Products & Inventory |
leads:read | Read | Tasks & Activity |
sales:read | Read | Quotes & Sales |
commissions:read | Read | Quotes & Sales |
inventory:read | Read | Products & Inventory |
samples:read | Read | Products & Inventory |
documents:read | Read | Products & Inventory |
tokens:manage | Administration | Not offered on the key form |
contacts:write | Create and change | Not offered on the key form |
activities:write | Create and change | Not offered on the key form |
contacts:delete | Delete and restore | Delete and restore records |
activities:delete | Delete and restore | Delete and restore records |
opportunities:read | Read | Quotes & Sales |
opportunities:write | Create and change | Create and edit records |
manufacturers:write | Create and change | Create and edit records |
inventory:write | Create and change | Create and edit records |
inventory:delete | Delete and restore | Delete and restore records |
line_sheets:write | Create and change | Create and edit records |
samples:write | Create and change | Create and edit records |
samples:delete | Delete and restore | Delete and restore records |
leads:write | Create and change | Create and edit records |
leads:delete | Delete and restore | Delete and restore records |
accounts:write | Create and change | Create and edit records |
products:write | Create and change | Create and edit records |
products:delete | Delete and restore | Delete and restore records |
opportunities:delete | Delete and restore | Delete and restore records |
accounts:delete | Delete and restore | Delete and restore records |
manufacturers:delete | Delete and restore | Delete and restore records |
price_sheets:write | Create and change | Create and edit records |
multiplier:write | Create and change | Create and edit records |
inventory:stock | Action | Create and edit records |
quotes:write | Create and change | Create and edit records |
quotes:delete | Delete and restore | Delete and restore records |
Key and health
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1 | Any valid key |
| GET | /api/v1/health | Any valid key |
| GET | /api/v1/deletions | The read scope of the resource named by `resource` |
| GET | /api/v1/tokens | tokens:manage |
| DELETE | /api/v1/tokens/{id} | tokens:manage |
| GET | /api/v1/price-book/price-sheets | price_sheets:write |
| POST | /api/v1/price-book/price-sheets | price_sheets:write |
| PATCH | /api/v1/price-book/price-sheets/{id} | price_sheets:write |
| GET | /api/v1/price-book/price-sheet-items | price_sheets:write |
| POST | /api/v1/price-book/price-sheet-items | price_sheets:write |
| PATCH | /api/v1/price-book/price-sheet-items/{id} | price_sheets:write |
| GET | /api/v1/price-book/spas | multiplier:write |
| POST | /api/v1/price-book/spas | multiplier:write |
| PATCH | /api/v1/price-book/spas/{id} | multiplier:write |
| GET | /api/v1/price-book/manufacturer-rates | multiplier:write |
| PATCH | /api/v1/price-book/manufacturer-rates/{id} | multiplier:write |
| GET | /api/v1/price-book/tariff-surcharges | multiplier:write |
| POST | /api/v1/price-book/tariff-surcharges | multiplier:write |
| PATCH | /api/v1/price-book/tariff-surcharges/{id} | multiplier:write |
| GET | /api/v1/price-book/tariff-origins | multiplier:write |
| POST | /api/v1/price-book/tariff-origins | multiplier:write |
| PATCH | /api/v1/price-book/tariff-origins/{id} | multiplier:write |
| GET | /api/v1/price-book/tariff-duty-rules | multiplier:write |
| POST | /api/v1/price-book/tariff-duty-rules | multiplier:write |
| PATCH | /api/v1/price-book/tariff-duty-rules/{id} | multiplier:write |
Companies
Reads need accounts:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/accounts | accounts:read |
| GET | /api/v1/accounts/{id} | accounts:read |
| GET | /api/v1/search/accounts | accounts:read |
| GET | /api/v1/accounts/{id}/contacts | accounts:read |
| POST | /api/v1/accounts | accounts:write |
| PATCH | /api/v1/accounts/{id} | accounts:write |
| DELETE | /api/v1/accounts/{id} | accounts:delete |
| POST | /api/v1/accounts/{id}/contacts | accounts:write |
| DELETE | /api/v1/accounts/{id}/contacts/{contactId} | accounts:write |
| Field | Type | Written by | Returned by | Notes |
|---|---|---|---|---|
id | uuid; not null | no (set by the server) | list, detail, write | The company's id. |
workspace_id | uuid | no (set by the server) | list, detail, write | The token's workspace. |
account_name | text, ≤ 200 chars; not null | create, update; required on create | list, detail, write | The company's name (a rename follows onto its quotes, activities, contacts, sales and commissions). Required on create and never blank. |
account_status | text, ≤ 200 chars | create, update | list, detail, write | Status, as text (Active, Prospect, Inactive, ...). |
primary_phone | phone, ≤ 40 chars | create, update | list, detail, write | Main phone number. A new or changed number must be a phone number: a US or Canadian number (10 digits, or 1 and 10 digits) or an international number starting with +. |
street_address | text, ≤ 200 chars | create, update | detail, write | Street address. |
street_address_2 | text, ≤ 200 chars | create, update | detail, write | Street address, second line. |
city | text, ≤ 200 chars | create, update | list, detail, write | City. |
state | text, ≤ 200 chars | create, update | list, detail, write | State or region. |
zip_code | text, ≤ 20 chars | create, update | list, detail, write | Postal code. |
territory | text, ≤ 200 chars | create, update | list, detail, write | The company's territory, as the app types it (the territory code follows it unless territory_code is sent too). The company must stay inside the token owner's territories (its account manager or creator arm aside); moving it also moves its sales, which needs sales edit and every moved sale in the owner's sales scope. |
territory_code | text, ≤ 200 chars | create, update | list, detail, write | The territory's code. On create, a territory-restricted owner sends it only together with territory (else 400); on an update it is kept as sent, and the company must stay inside the owner's territories (403). |
zone | text, ≤ 200 chars | create, update | list, detail, write | Zone. |
mfg_int | text, ≤ 200 chars | create, update | detail, write | Manufacturer interest, as text. |
account_manager | text, ≤ 200 chars | create, update | list, detail, write | The company's account manager, as the app shows it (a name, email or user id: free text). A member's key may leave it blank or set its owner, never another active member (403); with the workspace's account-manager auto-fill on, the company's sales, quotes and commissions follow it. |
type | text, ≤ 200 chars | create, update | list, detail, write | Company type. |
created_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the company was created. |
updated_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the company last changed: send it back as PATCH's expected_updated_at. |
parent_account_id | uuid | create, update | detail, write | The parent company (a branch's head office); null detaches it. A company the token's owner can read and edit (else 404 Linked record not found, or 403), never the company itself or one of its own descendants (422 parent_loop). |
is_master_account | boolean | create, update | detail, write | Whether the company is a master account. |
ship_to_name | text, ≤ 200 chars | create, update | detail, write | Ship-to name. |
ship_to_attention | text, ≤ 200 chars | create, update | detail, write | Ship-to attention line. |
ship_to_street_address | text, ≤ 200 chars | create, update | detail, write | Ship-to street address. |
ship_to_city | text, ≤ 200 chars | create, update | detail, write | Ship-to city. |
ship_to_state | text, ≤ 200 chars | create, update | detail, write | Ship-to state or region. |
ship_to_zip_code | text, ≤ 20 chars | create, update | detail, write | Ship-to postal code. |
ship_to_country | text, ≤ 200 chars | create, update | detail, write | Ship-to country. |
Contacts
Reads need contacts:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/contacts | contacts:read |
| GET | /api/v1/contacts/{id} | contacts:read |
| GET | /api/v1/search/contacts | contacts:read |
| GET | /api/v1/contacts/{id}/accounts | contacts:read |
| POST | /api/v1/contacts | contacts:write |
| PATCH | /api/v1/contacts/{id} | contacts:write |
| DELETE | /api/v1/contacts/{id} | contacts:delete |
| POST | /api/v1/contacts/{id}/restore | contacts:delete |
| Field | Type | Written by | Returned by | Notes |
|---|---|---|---|---|
id | uuid | no (set by the server) | list, detail, write | The contact's id. |
workspace_id | uuid | no (set by the server) | list, detail, write | The token's workspace. |
first_name | text, ≤ 200 chars | create, update | list, detail, write | First name. |
last_name | text, ≤ 200 chars | create, update | list, detail, write | Last name. |
company_name | text, ≤ 200 chars | create, update | list, detail, write | Company name as text. |
phone | phone, ≤ 40 chars | create, update | list, detail, write | Phone number. A US or Canadian number (10 digits, or 1 and 10 digits) or an international number starting with +. Any other new or changed value (letters, an extension, a 7-digit local number) is a 400; the contact's own stored number sent back unchanged is accepted and left as stored. |
title | text, ≤ 200 chars | create, update | list, detail, write | Job title. |
address | text, ≤ 500 chars | create, update | write | Street address. |
city | text, ≤ 200 chars | create, update | write | City. |
state | text, ≤ 200 chars | create, update | write | State or region. |
zip_code | text, ≤ 20 chars | create, update | write | Postal code. |
full_name | text, ≤ 400 chars | create, update | list, detail, write | Split into first_name / last_name when those are absent. |
email | email, ≤ 320 chars | create, update | list, detail, write | Email address. A write takes name@domain.tld: any other new or changed value is a 400; the contact's own stored email sent back unchanged is accepted when it has the name@domain shape. |
tags | array of strings, ≤ 50 of ≤ 100 chars | create, update | write | Up to 50 tags, deduplicated. |
account_id | uuid | no | list, detail, write | The contact's primary company. Sending it is a 400: "Link a contact to a company with POST /api/v1/accounts/{id}/contacts." |
created_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the contact was created. |
updated_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the contact last changed. |
| Filter field | Operators | Sort | Value |
|---|---|---|---|
email | eq | — | exact text |
account_id | eq, in, is_null | — | uuid |
created_at | gte, lte | created_at, -created_at | ISO 8601 date or timestamp (send + as %2B); a date alone is a whole UTC day: gte from its start, lte through its end |
updated_at | gte, lte | updated_at, -updated_at | ISO 8601 date or timestamp (send + as %2B); a date alone is a whole UTC day: gte from its start, lte through its end |
Quotes
Reads need quotes:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/quotes | quotes:read |
| GET | /api/v1/quotes/{id} | quotes:read |
| GET | /api/v1/search/quotes | quotes:read |
| GET | /api/v1/quotes/{id}/items | quotes:read |
| POST | /api/v1/quotes | quotes:write |
| PATCH | /api/v1/quotes/{id} | quotes:write |
| DELETE | /api/v1/quotes/{id} | quotes:delete |
| POST | /api/v1/quotes/{id}/restore | quotes:delete |
The fields of this resource are listed in the OpenAPI description.
Activities
Reads need activities:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/activities | activities:read |
| GET | /api/v1/activities/{id} | activities:read |
| POST | /api/v1/activities | activities:write |
| PATCH | /api/v1/activities/{id} | activities:write |
| DELETE | /api/v1/activities/{id} | activities:delete |
| POST | /api/v1/activities/{id}/restore | activities:delete |
| Field | Type | Written by | Returned by | Notes |
|---|---|---|---|---|
id | uuid | no (set by the server) | list, detail, write | The activity's id. |
workspace_id | uuid | no (set by the server) | list, detail, write | The token's workspace. |
type | text, ≤ 80 chars | create, update | list, detail, write | Activity type, e.g. Call, Email, Meeting, Task, Note. Defaults to Task. |
subject | text, ≤ 200 chars | create, update | list, detail, write | Subject line. |
activity_name | text, ≤ 200 chars | create, update | list, detail, write | Activity name. |
description | text, ≤ 10000 chars | create, update | detail, write | Notes, as plain text (newlines kept). |
account | text, ≤ 200 chars | create, update | list, detail, write | Company name as text. |
contact | text, ≤ 200 chars | create, update | list, detail, write | Contact name as text. |
manufacturer | text, ≤ 200 chars | create, update | list, detail, write | Manufacturer name as text. |
end_user | text, ≤ 200 chars | create, update | list, detail, write | End user name. |
end_user_contact | text, ≤ 200 chars | create, update | list, detail, write | End user contact name. |
manufacturer_contact | text, ≤ 200 chars | create, update | list, detail, write | Manufacturer contact name. |
assigned_to | uuid | create, update | list, detail, write | A user id in the token's workspace. |
account_id | uuid | create, update | list, detail, write | The linked company. |
contact_id | uuid | create, update | list, detail, write | The linked contact. |
manufacturer_id | uuid | create, update | list, detail, write | The linked manufacturer. |
quote_id | uuid | create, update | list, detail, write | The linked quote. |
sale_id | uuid | create, update | list, detail, write | The linked sale. |
activity_date | ISO 8601 date | create, update | list, detail, write | ISO 8601 date or timestamp. |
due_date | ISO 8601 date | create, update | list, detail, write | ISO 8601 date or timestamp. |
reminder_at | ISO 8601 timestamp | create, update | list, detail, write | ISO 8601 date or timestamp. |
completed | boolean | update | list, detail, write | Whether the activity is done; empty (null) counts as not done in the app. |
priority | low / medium / high (any case); or null ("" too) | update | list, detail, write | low, medium or high (any case); empty clears it, and an empty priority (null) counts as medium in the app. |
body | text | no | detail, write | Synced mail body, as plain text. |
updated_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the activity last changed. |
created_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the activity was created. |
| Filter field | Operators | Sort | Value |
|---|---|---|---|
type | eq | — | exact text |
assigned_to | eq | — | uuid |
account_id | eq, in, is_null | — | uuid |
contact_id | eq, in, is_null | — | uuid |
manufacturer_id | eq, in, is_null | — | uuid |
quote_id | eq, in, is_null | — | uuid |
sale_id | eq, in, is_null | — | uuid |
due_date | gte, lte, is_null | due_date, -due_date (empty last) | date, YYYY-MM-DD |
completed | eq, is_null | — | true / false |
priority | eq, in, is_null | — | one of low / medium / high (exact) |
updated_at | gte, lte | updated_at, -updated_at | ISO 8601 date or timestamp (send + as %2B); a date alone is a whole UTC day: gte from its start, lte through its end |
created_at | gte, lte | created_at, -created_at | ISO 8601 date or timestamp (send + as %2B); a date alone is a whole UTC day: gte from its start, lte through its end |
Manufacturers
Reads need manufacturers:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/manufacturers | manufacturers:read |
| GET | /api/v1/manufacturers/{id} | manufacturers:read |
| POST | /api/v1/manufacturers | manufacturers:write |
| PATCH | /api/v1/manufacturers/{id} | manufacturers:write |
| DELETE | /api/v1/manufacturers/{id} | manufacturers:delete |
| Field | Type | Written by | Returned by | Notes |
|---|---|---|---|---|
id | uuid; not null | no (set by the server) | list, detail, write | The manufacturer's id. |
workspace_id | uuid | no (set by the server) | list, detail, write | The token's workspace. |
manufacturer_name | text, ≤ 200 chars; not null | create, update; required on create | list, detail, write | The manufacturer's name, unique in the workspace (a rename follows onto its quotes, sales, activities, commissions, products, inventory and the opportunities' principal lists). One name: a semicolon, a vertical bar or a line break in it is a 400 (the territory / manufacturer scope reads them as separators between names), and a name another manufacturer in the workspace has ignoring case, a soft-deleted one included, is a 409. |
parent_manufacturer_id | uuid | no | list, detail, write | The parent manufacturer (set in the app). |
terms_and_conditions | text, ≤ 10000 chars | create, update | list, detail, write | Terms and conditions, as plain text (newlines kept). |
mfg_territory | text, ≤ 200 chars | create, update | list, detail, write | The territory the manufacturer belongs to; it must stay inside the token owner's territories. |
expected_sales | amount, ≤ 2 decimals, ≥ 0, ≤ 1000000000000; or null ("" too) | create, update | list, detail, write | Expected annual sales. |
primary_phone | phone, ≤ 40 chars | create, update | list, detail, write | Main phone number. |
city | text, ≤ 200 chars | create, update | list, detail, write | City. |
state | text, ≤ 200 chars | create, update | list, detail, write | State or region. |
zip_code | text, ≤ 20 chars | create, update | list, detail, write | Postal code. |
regional_manager | text, ≤ 200 chars | create, update | list, detail, write | The manufacturer's regional manager, as text. |
updated_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the manufacturer last changed. Send it back as PATCH's expected_updated_at. |
| Filter field | Operators | Sort | Value |
|---|---|---|---|
id | — | id, -id | uuid |
parent_manufacturer_id | eq, in, is_null | — | uuid |
mfg_territory | eq | — | exact text |
Products
Reads need products:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/products | products:read |
| GET | /api/v1/products/{id} | products:read |
| POST | /api/v1/products | products:write |
| PATCH | /api/v1/products/{id} | products:write |
| DELETE | /api/v1/products/{id} | products:delete |
| POST | /api/v1/products/{id}/restore | products:delete |
| Field | Type | Written by | Returned by | Notes |
|---|---|---|---|---|
id | uuid | no (set by the server) | list, detail, write | The product's id. |
workspace_id | uuid | no (set by the server) | list, detail, write | The token's workspace. |
stock | boolean; or null | create, update | list, detail, write | Whether the product is a stocked item. true, false or null. |
manufacturer_name | text, ≤ 200 chars; not null | create, update; required on create | list, detail, write | The manufacturer's name as text: with part_number, the product's catalog key in the workspace, and what a token owner limited to some manufacturers is matched by. Required on create, never empty; one name (no semicolon, vertical bar or line break). The product must stay inside the token owner's manufacturer scope (403 otherwise). |
part_number | text, ≤ 200 chars; not null | create, update; required on create | list, detail, write | Part number: with manufacturer_name, the product's catalog key in the workspace. Required on create, never empty. |
product_family | text, ≤ 200 chars | create, update | list, detail, write | Product family. |
product_line | text, ≤ 200 chars | create, update | list, detail, write | Product line. |
root_part_no | text, ≤ 200 chars | create, update | list, detail, write | Root part number. |
list_price | amount, ≤ 4 decimals, ≥ 0, ≤ 9999999999; or null | create, update | list, detail, write | List price. A number (or decimal text) from 0, at most 4 decimal places. Setting it (create) or changing it (update) needs the token owner's Edit pricing ability (403 otherwise); null sets no price. |
stock_price | amount, ≤ 4 decimals, ≥ 0, ≤ 9999999999; or null | create, update | list, detail, write | Stock price. A number (or decimal text) from 0, at most 4 decimal places. Setting it (create) or changing it (update) needs the token owner's Edit pricing ability (403 otherwise); null sets no price. |
commission_rate | amount, ≤ 6 decimals, ≥ 0, ≤ 1; or null | create, update | list, detail, write | Commission rate as a fraction (0.08 = 8%). A number (or decimal text) from 0 to 1, at most 6 decimal places (0.08 = 8%; "8%" is refused). Setting it (create) or changing it (update) needs the token owner's Edit pricing ability (403 otherwise); null sets no price. |
lead_time | text, ≤ 200 chars | create, update | list, detail, write | Lead time as text. |
upc | text, ≤ 200 chars | create, update | list, detail, write | UPC code. |
uom | text, ≤ 50 chars | create, update | list, detail, write | Unit of measure. |
hou_stock | integer, ≥ 0, ≤ 2147483647; or null | create, update | list, detail, write | Stock on hand in the first location stock column. Usually 0 or empty in a workspace that does not record stock here. A whole number from 0, or null. |
pharr_stock | integer, ≥ 0, ≤ 2147483647; or null | create, update | list, detail, write | Stock on hand in the second location stock column. Usually 0 or empty in a workspace that does not record stock here. A whole number from 0, or null. |
reorder_level | integer, ≥ 0, ≤ 2147483647; or null | create, update | list, detail, write | Reorder level. A whole number from 0, or null. |
shipping_weight | amount, ≤ 4 decimals, ≥ 0, ≤ 1000000; or null | create, update | list, detail, write | Shipping weight. A number (or decimal text) from 0 to 1,000,000, at most 4 decimal places, or null. |
unit_cost | amount, ≤ 4 decimals, ≥ 0, ≤ 9999999999; or null | create, update | list, detail, write | Unit cost. Returned only to a token whose owner is an admin or a manager (has reports). Only for a token whose owner is an admin or a manager (for any other owner it is an unknown field: 400). A number (or decimal text) from 0, at most 4 decimal places. Setting it (create) or changing it (update) needs the token owner's Edit pricing ability (403 otherwise); null sets no price. |
description | text, ≤ 2000 chars | create, update | list, detail, write | Description. |
price_code | text, ≤ 50 chars | create, update | list, detail, write | Price code. Compared as text: "a" is a change from "A". Setting it (create) or changing it (update) needs the token owner's Edit pricing ability (403 otherwise); null sets no price. |
image_url | http(s) URL (no control or invisible character), ≤ 2000 chars | create, update | list, detail, write | Image link (http or https). |
specification_url | http(s) URL (no control or invisible character), ≤ 2000 chars | create, update | list, detail, write | Specification sheet link (http or https). |
notes | text, ≤ 10000 chars | create, update | list, detail, write | Notes. |
created_time | text | no | list, detail, write | When the product was created, as a legacy import recorded it (text; read only). |
modified_time | text | no | list, detail, write | When the product was last modified, as a legacy import recorded it (text; read only). |
created_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the product was created. |
updated_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the product last changed. |
Leads
Reads need leads:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/leads | leads:read |
| GET | /api/v1/leads/{id} | leads:read |
| POST | /api/v1/leads | leads:write |
| PATCH | /api/v1/leads/{id} | leads:write |
| DELETE | /api/v1/leads/{id} | leads:delete |
| POST | /api/v1/leads/{id}/restore | leads:delete |
| POST | /api/v1/leads/{id}/convert | leads:write |
| Field | Type | Written by | Returned by | Notes |
|---|---|---|---|---|
id | uuid | no (set by the server) | list, detail, write | The lead's id. |
workspace_id | uuid | no (set by the server) | list, detail, write | The token's workspace. |
company | text, ≤ 200 chars | create, update | list, detail, write | The company the lead is for. Defaults to an empty string; null stores an empty string. |
contact_name | text, ≤ 200 chars | create, update | list, detail, write | The person at the company. |
title | text, ≤ 200 chars | create, update | list, detail, write | Their job title. |
email | email, ≤ 320 chars | create, update | list, detail, write | Their email address. |
phone | phone, ≤ 50 chars | create, update | list, detail, write | Their phone number. |
source | text, ≤ 120 chars | create, update | list, detail, write | Where the lead came from (Website, Trade Show, Referral, ...). |
status | New / Attempting Contact / Qualified / Quoted / Lost / Disqualified / No Response | create, update | list, detail, write | Where the lead stands. Defaults to New. Converted is set only by POST /api/v1/leads/{id}/convert. |
priority | Hot / Warm / Cold | create, update | list, detail, write | Hot, Warm or Cold. Defaults to Warm. |
assigned_to | uuid | create, update | list, detail, write | The user who owns the lead (a user id in the token's workspace). Empty unless set. An active user of the workspace (else 404 Linked record not found, or 422 assignee_inactive); a member's token may not assign a lead to another member (403). |
territory | text, ≤ 120 chars | create, update | list, detail, write | The lead's territory, as text (a territory code or name). The lead must stay inside the token owner's lead scope (403). |
manufacturer_interest | text, ≤ 1000 chars | create, update | list, detail, write | The manufacturers the lead is interested in, as text (names separated by semicolons). |
account_id | uuid | create, update | list, detail, write | The company record the lead is linked to. One the token's owner can read (else 404 Linked record not found). null unlinks. |
contact_id | uuid | create, update | list, detail, write | The lead's primary contact record. One the token's owner can read (else 404 Linked record not found). null unlinks. |
manufacturer_id | uuid | create, update | list, detail, write | The manufacturer record the lead is linked to. One the token's owner can read (else 404 Linked record not found). null unlinks. |
next_follow_up | ISO 8601 date | create, update | list, detail, write | The next follow-up date (YYYY-MM-DD). |
estimated_value | amount, ≤ 2 decimals, ≥ 0, ≤ 9999999999.99 | create, update | list, detail, write | The lead's estimated value. Defaults to 0. |
notes | text, ≤ 10000 chars | create, update | list, detail, write | Notes, as plain text. |
last_touch_at | ISO 8601 timestamp | no (set by the server) | list, detail, write | When the lead was last touched (set by the server). |
created_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the lead was created. |
updated_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the lead last changed. |
Sales
Reads need sales:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/sales | sales:read |
| GET | /api/v1/sales/{id} | sales:read |
The fields of this resource are listed in the OpenAPI description.
Commissions
Reads need commissions:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/commissions | commissions:read |
| GET | /api/v1/commissions/{id} | commissions:read |
The fields of this resource are listed in the OpenAPI description.
Inventory
Reads need inventory:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/inventory | inventory:read |
| GET | /api/v1/inventory/{id} | inventory:read |
| GET | /api/v1/inventory/{id}/movements | inventory:read |
| GET | /api/v1/inventory/{id}/balances | inventory:read |
| GET | /api/v1/inventory/locations | inventory:read |
| POST | /api/v1/inventory | inventory:write |
| PATCH | /api/v1/inventory/{id} | inventory:write |
| DELETE | /api/v1/inventory/{id} | inventory:delete |
| POST | /api/v1/inventory/{id}/restore | inventory:delete |
| POST | /api/v1/inventory/{id}/movements | inventory:stock |
| POST | /api/v1/inventory/{id}/move | inventory:stock |
| PATCH | /api/v1/inventory/{id}/balances | inventory:stock |
| POST | /api/v1/inventory/locations | inventory:stock |
| Field | Type | Written by | Returned by | Notes |
|---|---|---|---|---|
id | uuid | no (set by the server) | list, detail, write | The item's id. |
workspace_id | uuid | no (set by the server) | list, detail, write | The token's workspace. |
item_name | text, ≤ 200 chars | create, update | list, detail, write | Item name. |
manufacturer | text, ≤ 200 chars | create, update | list, detail, write | Manufacturer name as text; must be in the key owner's manufacturer scope when they have one. |
part_number | text, ≤ 200 chars | create, update | list, detail, write | Part number. |
description | text, ≤ 2000 chars | create, update | list, detail, write | Description. |
houston_stock | integer, ≥ 0, ≤ 1000000 | create, update | list, detail, write | Stock on hand, a whole number. Defaults to 0 on create. On an ERP-linked item a change is a count at the ERP's main location and needs expected_houston_stock. |
bin_location | text, ≤ 200 chars | create, update | list, detail, write | Bin or shelf location. |
assigned_to | text, ≤ 200 chars | create, update | list, detail, write | Who holds the item, as the name the app shows (the own-records lock reads it). |
last_modified_by | text | no (set by the server) | list, detail, write | Who last changed the item (set by the server). |
created_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the item was created. |
updated_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the item last changed. |
Samples
Reads need samples:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/samples | samples:read |
| GET | /api/v1/samples/{id} | samples:read |
| POST | /api/v1/samples | samples:write |
| PATCH | /api/v1/samples/{id} | samples:write |
| DELETE | /api/v1/samples/{id} | samples:delete |
| POST | /api/v1/samples/{id}/restore | samples:delete |
| Field | Type | Written by | Returned by | Notes |
|---|---|---|---|---|
id | uuid | no (set by the server) | list, detail, write | The sample's id. |
workspace_id | uuid | no (set by the server) | list, detail, write | The token's workspace. |
manufacturer | text, ≤ 200 chars | create, update | list, detail, write | Manufacturer name as text. |
part_number | text, ≤ 200 chars | create, update | list, detail, write | Part number. |
description | text, ≤ 2000 chars | create, update | list, detail, write | Description. |
location | text, ≤ 200 chars | create, update | list, detail, write | Where the sample is kept (a city or office). |
houston_stock | integer, ≥ 0, ≤ 2147483647 | create, update | list, detail, write | How many are on hand, a whole number. Defaults to 0 on create. |
bin_location | text, ≤ 200 chars | create, update | list, detail, write | Bin or shelf location. |
assigned_to | text, ≤ 200 chars | create, update | list, detail, write | Who has the sample checked out, as the name the app shows (free text). Setting it checks the sample out now; clearing it checks it back in. |
workflow_state | available / requested / approved / checked_out / returned / lost / damaged | create, update | list, detail, write | Where the sample is in its checkout workflow. lost, damaged, requested and approved are kept as sent; any other state is set from assigned_to (checked_out when someone has it, else available), as the app does. |
linked_account_id | uuid | create, update | list, detail, write | The company the sample is for: one the token's owner can read (else 404 Linked record not found; an owner without accounts read: 403, whatever the id). null unlinks. |
linked_contact_id | uuid | create, update | list, detail, write | The contact the sample is for: one the token's owner can read (else 404 Linked record not found; an owner without contacts read: 403, whatever the id). null unlinks. |
updated_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the sample last changed. |
created_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the sample was created. |
Line Sheets
Reads need documents:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/line-sheets | documents:read |
| GET | /api/v1/line-sheets/{id} | documents:read |
| POST | /api/v1/line-sheets | line_sheets:write |
| PATCH | /api/v1/line-sheets/{id} | line_sheets:write |
| Field | Type | Written by | Returned by | Notes |
|---|---|---|---|---|
id | uuid | no (set by the server) | list, detail, write | The line sheet's id. |
workspace_id | uuid | no (set by the server) | list, detail, write | The token's workspace. |
title | text, ≤ 200 chars; not null | create, update; required on create | list, detail, write | The line sheet's title. |
reference_code | text, ≤ 64 chars; not null | create, update | list, detail, write | Stored in upper case; any run of characters other than A-Z, 0-9, ., _ and - becomes one -. Made from the title when a create leaves it out. On create a code another sheet in the workspace has gets the next free -2, -3, ... suffix; on update it is a 409. |
description | text, ≤ 2000 chars | create, update | list, detail, write | A description, as plain text. |
category | text, ≤ 200 chars | create, update | list, detail, write | Category, e.g. Territory Line Sheet, Product Brochure, Stock Sheet. |
product_family | text, ≤ 200 chars | create, update | list, detail, write | Product family. |
territory_codes | array of strings, ≤ 50 of ≤ 100 chars | create, update | list, detail, write | Territory codes, stored in upper case; an entry holding commas, semicolons or line breaks is split. |
tags | array of strings, ≤ 50 of ≤ 100 chars | create, update | list, detail, write | Tags; an entry holding commas, semicolons or line breaks is split. |
manufacturer_id | uuid | create, update | list, detail, write | The linked manufacturer: one in the token's workspace that is not deleted. |
source_kind | shared_path / external_url / uploaded_file | no (set by the server) | list, detail, write | Where the file is: external_url (a web link), shared_path (a file-share path, returned as its file name only) or uploaded_file (uploaded in the app). Always external_url for a sheet the API creates or re-points. |
source_url | https URL (no user name or password, no control or invisible character), ≤ 2000 chars; not null | create, update; required on create | list, detail, write | The web link. Another sheet in the workspace with the same link (ignoring case) is a 409. Sending it makes the sheet a web link; an uploaded sheet's file cannot be replaced through the API (422). |
file_name | text | no (set by the server) | list, detail, write | An uploaded sheet's file name. |
mime_type | text | no (set by the server) | list, detail, write | An uploaded sheet's file type. |
file_size | integer | no (set by the server) | list, detail, write | An uploaded sheet's size in bytes. |
active | boolean | create, update | list, detail, write | false archives the sheet (archived_at is stamped); true brings it back. A new sheet is active unless the create says false. |
featured | boolean | create, update | list, detail, write | Whether the sheet is featured. Defaults to false. |
created_by_name | text | no (set by the server) | list, detail, write | Who created the sheet. |
created_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the sheet was created. |
updated_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the sheet last changed. |
archived_at | ISO 8601 timestamp | no (set by the server) | list, detail, write | When the sheet was archived (empty while active). |
| Filter field | Operators | Sort | Value |
|---|---|---|---|
manufacturer_id | eq, in, is_null | — | uuid |
source_kind | eq, in | — | one of shared_path / external_url / uploaded_file (exact) |
active | eq | — | true / false |
featured | eq | — | true / false |
created_at | gte, lte | created_at, -created_at | ISO 8601 date or timestamp (send + as %2B); a date alone is a whole UTC day: gte from its start, lte through its end |
updated_at | gte, lte | updated_at, -updated_at | ISO 8601 date or timestamp (send + as %2B); a date alone is a whole UTC day: gte from its start, lte through its end |
Team Directory
Any valid key can read these.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/users | Any valid key |
| GET | /api/v1/users/{id} | Any valid key |
| Field | Type | Returned by | Notes |
|---|---|---|---|
id | uuid | list, detail | The user's id (an assigned_to id names it). |
user_name | text | list, detail | The user's display name. |
email | list, detail | The user's email address. | |
status | text; not null | list, detail | The user's status as stored (Active, Inactive, ...); active says whether it lets them sign in. |
created_at | ISO 8601 timestamp; not null | list, detail | When the user was added. |
updated_at | ISO 8601 timestamp; not null | list, detail | When the user last changed. |
| Filter field | Operators | Sort | Value |
|---|---|---|---|
status | eq | — | exact text |
created_at | — | created_at, -created_at | ISO 8601 date or timestamp (send + as %2B); a date alone is a whole UTC day: gte from its start, lte through its end |
updated_at | — | updated_at, -updated_at | ISO 8601 date or timestamp (send + as %2B); a date alone is a whole UTC day: gte from its start, lte through its end |
Opportunities
Reads need opportunities:read.
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/opportunities | opportunities:read |
| GET | /api/v1/opportunities/{id} | opportunities:read |
| POST | /api/v1/opportunities | opportunities:write |
| PATCH | /api/v1/opportunities/{id} | opportunities:write |
| DELETE | /api/v1/opportunities/{id} | opportunities:delete |
| POST | /api/v1/opportunities/{id}/restore | opportunities:delete |
| PUT | /api/v1/opportunities/{id}/quotes/{quoteId} | opportunities:write |
| DELETE | /api/v1/opportunities/{id}/quotes/{quoteId} | opportunities:write |
| Field | Type | Written by | Returned by | Notes |
|---|---|---|---|---|
name | text, ≤ 200 chars; not null | create, update; required on create | list, detail, write | The project's name. |
account_id | uuid | create, update | list, detail, write | The project's company (a company id in the token's workspace), or null. |
contact_id | uuid | create, update | list, detail, write | The project's contact (a contact id the key can read), or null. |
manufacturers | array of strings, ≤ 25 of ≤ 200 chars | create, update | list, detail, write | Manufacturer names the project is for (deduplicated); they decide its manufacturer scope. |
estimated_value | amount, ≤ 2 decimals, ≥ 0, ≤ 999999999999.99 | create, update | list, detail, write | The estimate, in the workspace currency. Counted in the pipeline until a quote is linked. |
stage | text, ≤ 80 chars | create, update | list, detail, write | The stage, as configured in the workspace's project stages. |
win_probability | amount, ≤ 2 decimals, ≥ 0, ≤ 100; or null | create, update | list, detail, write | Win % (0 to 100), or null to follow the stage's percent. |
expected_close | ISO 8601 date | create, update | list, detail, write | Expected close date, YYYY-MM-DD. |
owner_user_id | uuid | create, update | list, detail, write | The owner (an active user in the token's workspace). A create without one is owned by the key's owner. |
source | text, ≤ 120 chars | create, update | list, detail, write | Where the project came from. |
next_step | text, ≤ 500 chars | create, update | list, detail, write | The next step. |
notes | text, ≤ 4000 chars | create, update | list, detail, write | Notes. |
lost_reason | text, ≤ 200 chars | create, update | list, detail, write | Why it was lost; kept only on a move into a Lost stage, cleared on any other stage. |
lost_competitor | text, ≤ 200 chars | create, update | list, detail, write | Who it was lost to; kept only with a Lost stage, like lost_reason. |
id | uuid | no (set by the server) | list, detail, write | The project's id. |
account | text | no (set by the server) | list, detail, write | The company's name, copied from the company. |
contact | text | no (set by the server) | list, detail, write | The contact's name, copied from the contact. |
closed_at | ISO 8601 timestamp | no (set by the server) | list, detail, write | When it was decided (Won or Lost), else null. |
created_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the project was created. |
updated_at | ISO 8601 timestamp; not null | no (set by the server) | list, detail, write | When the project last changed. |
| Filter field | Operators | Sort | Value |
|---|---|---|---|
account_id | eq, in, is_null | — | uuid |
contact_id | eq, in, is_null | — | uuid |
stage | eq, is_null | — | exact text |
expected_close | gte, lte, is_null | expected_close, -expected_close (empty last) | date, YYYY-MM-DD |
owner_user_id | eq, in, is_null | — | uuid |
created_at | — | created_at, -created_at | ISO 8601 date or timestamp (send + as %2B); a date alone is a whole UTC day: gte from its start, lte through its end |
updated_at | — | updated_at, -updated_at | ISO 8601 date or timestamp (send + as %2B); a date alone is a whole UTC day: gte from its start, lte through its end |
Support
Questions, or a request that does not behave as described here? Email support@jmcmahanllc.com with the request's requestId.