The BrytIO REST API reads and writes the records of one BrytIO workspace: contacts, companies, deals, quotes, invoices, activities, shipments, tags, email sequences and automations. Requests and responses are JSON over HTTPS, at the base URL below.
https://brytio.com/api/v1
Authentication
Every request carries an API key as a bearer token:
Authorization: Bearer <your API key>
An owner or admin creates keys in Settings > Developers. BrytIO shows a new key once and keeps only a hash of it, so copy the key when it is shown. A key belongs to one workspace and reads and writes only that workspace's records: an id from another workspace answers 404, as a missing one does.
The API and webhooks are included with the Team and Enterprise plans. On another plan every request is refused with 403 and "code": "plan_paused": nothing is changed, and the keys work again after an upgrade.
Scopes
A key has one of two kinds of access, chosen when it is created:
- Read and write (scopes
read and write): every endpoint. - Read only (scope
read): every GET. A POST, PATCH or DELETE answers 403.
GET /me returns the key's workspace and scopes, which makes it a good first call.
Requests and responses
Send a body as JSON with Content-Type: application/json. One record comes back as {"data": {...}}, and a list as {"data": [...], "count": 25, "nextCursor": "..."}, where count is the number of records on that page.
Dates and times are ISO 8601, in UTC. A date with no time of day (an invoice's dueDate, a quote's expiresAt, a deal's expectedClose) is stored as that date at midnight UTC: send 2026-11-30, or a date and time, which is read as the date it falls on in the workspace's time zone.
Responses can carry more fields than this reference lists. Build on the fields listed here: they are the ones BrytIO keeps.
Changed since
To keep another system in step, ask a list for what changed since your last sync with updated_since: an ISO 8601 date and time with a time zone, such as 2026-10-01T00:00:00Z or 2026-10-01T00:00:00-05:00. In a URL, write the + of an offset as %2B. A value without a time zone, or one that isn't a date and time, answers 400.
- The list holds the records whose
updatedAt is at or after that time, oldest change first (by updatedAt, then by id). - Page through it with
nextCursor, as above. The cursor records exactly where the page ended, so records that share a time are never skipped or repeated from one page to the next. It works only together with updated_since, and only on the list that gave it. - A record changed while you page moves to the end of the list and comes back there, with its new values.
- Start your next sync from the latest
updatedAt you received. Records changed at exactly that time come back again, so match records by id. A change that was still being saved while you read can carry a slightly earlier time: if you can't afford to miss one, start a minute earlier. - Deletions are not reported: a deleted record stops appearing, and nothing says it was deleted. Webhooks send the delete events.
- Some changes don't move
updatedAt, so updated_since doesn't return them. BrytIO keeps a contact's or company's score, a contact's lastContactedAt and a company's logoUrl up to date in the background. An automation's Update field action that sets a deal's title, value, probability, lineOfBusiness or notes leaves the deal's updatedAt as it was, so the deal doesn't look recently worked. To catch these, read the record itself, or the whole list now and then.
updated_since works on contacts, companies, deals, quotes, invoices, activities, shipments, email sequences and automations. Tags have no change time: read the whole list.
Rate limits
Each API key can make up to 120 requests a minute. All of a workspace's keys together can make up to 600 a minute, so more keys don't add more than that. An endpoint's answers carry three headers:
X-RateLimit-Limit: the requests a key may make in a minute. On an answer to a request that failed authentication, it is the failures its address may make instead.X-RateLimit-Remaining: how many more the key (or the address) can make in the current minute. When the workspace's keys together are nearer their 600, it is what is left of those.X-RateLimit-Reset: when the current minute ends, in Unix time (seconds).
Over a limit, a request answers 429 with a Retry-After header: wait that many seconds, then send it again.
Three answers come without these headers: 405 for a method an endpoint doesn't have, the automatic answer to an OPTIONS request, and an unexpected server error (500).
Requests that fail authentication (no key, or a key that is unknown or revoked) are limited by network address: 120 a minute from one address, and 120 a minute from all the addresses of one IPv6 network (a /64). Past that, until the minute is up, a request that fails authentication answers 429 instead of 401, and the address that sent the failures is refused whatever key it sends. A valid key sent from another address is not affected. So a key that has stopped working should not be retried in a loop.
Errors
An error answers with a JSON body whose error says in a sentence what went wrong, such as {"error": "firstName is required"}. A few errors also carry a code.
400: the request isn't valid (a field missing, of the wrong kind or out of range, or a parameter that isn't one). Nothing was changed.401: no API key was sent, or the key is unknown or revoked.402: the workspace's subscription is inactive, or it reached a limit of its plan (for example its number of contacts or invoices).403: the key has no write scope, the plan doesn't include the feature, or code is plan_paused: the plan doesn't include the API.404: no record with that id in your workspace.409: the request is valid, but the record's state refuses it (an accepted quote, a sent invoice), or the record changed while the request ran. Read it again and retry.429: too many requests (see Rate limits).
Webhooks
BrytIO can also send your systems an HTTP POST when something changes, such as a deal won or an invoice paid. An owner or admin sets webhooks up in Settings > Developers.
Read about webhooks in the docs.
Auth
Connection test and token inspection.
The org summary object
idstringnamestringplanstring
Check a key and see its scopes
GET/api/v1/me
Returns the key's workspace and scopes. A good first call when you connect a tool.
Responses
Can also answer 401, as described under Errors.
Companies
Company accounts with address and industry data.
The company object
idstringorganizationIdstringnamestringwebsitestring, or nulldomainstring, or nullread onlyThe website's domain, worked out from website.
logoUrlURL, or nullread onlyThe company's logo, when BrytIO found or was given one. A logo BrytIO finds in the background doesn't move updatedAt.
industrystring, or nullsizestring, or nullphonestring, or nulladdressLine1string, or nulladdressLine2string, or nullcitystring, or nullstatestring, or nullpostalCodestring, or nullcountrystring, or nullnotesstring, or nullbillingEmailemail address, or nullAP mailbox the company's invoices go to; wins over the billing contact's email.
billingContactIdstring, or nullThe contact (one of this company's) its invoices are addressed to.
portalCompanyAccessbooleanread onlyCompany-wide customer portal access. When true, the addresses in portalAccessEmails that still qualify (the billing email, the billing contact, or a contact marked CUSTOMER) can each see all of the invoices, quotes and contracts issued to the company (their companyId) and its statement in the customer portal. A colleague's quote or contract is read-only for them; only the person it was sent to can accept or sign it. Set by an owner or admin on the company page; not settable through the API.
portalAccessEmailsstringread onlyJSON array (as a string) of the lowercased addresses an owner or admin approved for company-wide portal access. Someone who newly qualifies (for example a new CUSTOMER contact or a changed billingEmail) is not added automatically; they wait on the company page for approval. Not settable through the API.
scoreinteger, or nullThe company's lead score. Changes without moving updatedAt.
createdAtdate and timeupdatedAtdate and time
List companies
GET/api/v1/companies
Newest first, or oldest change first with updated_since.
Parameters
limitintegerqueryHow many records a page holds, from 1 to 200 (50 when left out).
cursorstringqueryThe nextCursor of the page before. With updated_since, only a cursor from a list read with updated_since works.
updated_sincedate and timequeryOnly records changed at or after this time, oldest change first (by updatedAt, then id). An ISO 8601 date and time with a time zone, such as 2026-10-01T00:00:00Z; anything else answers 400. Deleted records are not reported.
qstringquerySearch. Every word must match the company's name, website or domain (any case, any part of the word).
Responses
Can also answer 400 and 401, as described under Errors.
Create a company
POST/api/v1/companies
Sends the company.created webhook and runs its automations.
Request body
Responses
201Company created
datacompany objectlinkedContactsintegerContacts with no company whose email is at this company's website domain, linked to it now (only when it is the one company in the workspace with that domain; personal and internet-provider mail never links, and a website that is a page on a shared platform, such as facebook.com/yourpage or linkedin.com/company/yours, gives the company no domain).
Can also answer 400, 401 and 403, as described under Errors.
Get a company
GET/api/v1/companies/{id}
Parameters
idstringin the pathrequiredThe record's id.
Responses
Can also answer 401 and 404, as described under Errors.
Update a company
PATCH/api/v1/companies/{id}
Only the fields sent change. Sends the company.updated webhook and runs its automations.
Parameters
idstringin the pathrequiredThe record's id.
Request body
Responses
Can also answer 400, 401, 403 and 404, as described under Errors.
Delete a company
DELETE/api/v1/companies/{id}
Contacts and deals at the company are unlinked and kept. Files attached to the company itself are deleted; files on its contacts and deals stay with them, and a file also attached to another record stays on that record. Sends the company.deleted webhook.
Parameters
idstringin the pathrequiredThe record's id.
Responses
200Resource deleted
dataobjectdata.idstringdata.deletedboolean
Can also answer 401, 403 and 404, as described under Errors.
GET/api/v1/companies/{id}/tags
Parameters
idstringin the pathrequiredThe record's id.
Responses
Can also answer 401 and 404, as described under Errors.
Tag a company
POST/api/v1/companies/{id}/tags
Send id to attach an existing tag, or name to attach the tag with that name, which is created when the workspace has none. Attaching a tag the company already has changes nothing.
Parameters
idstringin the pathrequiredThe record's id.
Request body
Send either id (an existing tag) or name.
idstringAn existing tag of this workspace.
namestringThe tag with this name, created when the workspace has none.
colorstringThe color of a tag created by name, such as #0ea5e9.
Responses
Can also answer 400, 401, 403 and 404, as described under Errors.
Remove a tag from a company
DELETE/api/v1/companies/{id}/tags
The tag itself stays in the workspace.
Parameters
Responses
200Tag removed
dataobjectdata.companyIdstringdata.tagIdstringdata.detachedboolean
Can also answer 400, 401, 403 and 404, as described under Errors.
Deals
Sales pipeline deals, on the default board or a custom one.
The deal object
idstringorganizationIdstringpipelineIdstring, or nullThe board the deal is on, or null for the default board.
titlestringvaluenumberstageone of LEAD, QUALIFIED, PROPOSAL, NEGOTIATION, WON, LOSTstageKeystring, or nullThe custom stage the deal is in, when its board has custom stages. stage then holds the standard stage at the same position, for integrations that know only the six.
probabilitynumber0 to 100. WON deals are always 100 and LOST deals always 0: a probability written to a closed deal is replaced by that value.
notesstring, or nullcompanyIdstring, or nullcompanyobject, or nullThe company's id and name, on reads and updates.
company.idstringcompany.namestringcontactIdstring, or nullcontactobject, or nullThe contact's id and name, on reads and updates.
contact.idstringcontact.firstNamestringcontact.lastNamestring, or nullownerIdstring, or nullThe member who owns the deal (a user id).
expectedClosedate and time, or nullclosedAtdate and time, or nullWhen the deal was won or lost. null while it is open.
stageEnteredAtdate and time, or nullWhen the deal entered its current stage. Set on create and on every stage change; null on deals that predate the field.
closeReasonstring, or nullWhy a WON or LOST deal closed: one of the workspace's reasons (Settings > CRM > Close reasons) or "Other". null on open deals, on deals closed without one, and cleared when a deal is reopened.
closeNotestring, or nullThe note given with the close reason. Cleared on reopen.
closedFromStagestring, or nullread onlyThe open stage key the deal left when it closed (where the app's Reopen puts it back). Server-set.
lineOfBusinessstring, or nullread onlyThe deal's line of business, for revenue reports.
sourceone of manual, rebillread onlyWhere the deal came from. "rebill" marks the Won deal vendor-bill ingest creates per customer per distributor bill; everything else is "manual". Server-set: not accepted on create or update.
createdAtdate and timeupdatedAtdate and timeWhen the deal last changed. An automation's Update field action that sets title, value, probability, lineOfBusiness or notes doesn't move it (the deal shouldn't look recently worked), so updated_since doesn't return that change.
List deals
GET/api/v1/deals
Most recently updated first, or oldest change first with updated_since.
Parameters
limitintegerqueryHow many records a page holds, from 1 to 200 (50 when left out).
cursorstringqueryThe nextCursor of the page before. With updated_since, only a cursor from a list read with updated_since works.
updated_sincedate and timequeryOnly records changed at or after this time, oldest change first (by updatedAt, then id). An ISO 8601 date and time with a time zone, such as 2026-10-01T00:00:00Z; anything else answers 400. Deleted records are not reported.
stagestringqueryOnly deals in this stage: one of LEAD, QUALIFIED, PROPOSAL, NEGOTIATION, WON, LOST, or the key of a custom stage on a board (which matches open deals only).
Responses
Can also answer 400 and 401, as described under Errors.
Create a deal
POST/api/v1/deals
The deal goes on the workspace's default board. Sends the deal.created webhook and runs its automations.
Request body
titlestringrequiredvaluenumberstagestringOne of LEAD, QUALIFIED, PROPOSAL, NEGOTIATION, WON, LOST, or the key of a custom stage on the default board (400 otherwise).
probabilitynumber0 to 100, else 400. Left out, the deal takes its stage's default probability (stock board: LEAD 10, QUALIFIED 25, PROPOSAL 50, NEGOTIATION 75; a board can set its own). WON is always 100 and LOST always 0, even when a probability is sent.
notesstringcompanyIdstringcontactIdstringownerIdstring, or nullThe member who owns the deal (a user id of this workspace; 400 otherwise).
closeReasonstring, or nullOptional. Only with a WON or LOST stage (a value with an open stage answers 400; null or "" is ignored). Must be one of the workspace's reasons for that outcome, or "Other" with a closeNote, else 400.
closeNotestring, or nullOptional note with the close reason (WON or LOST only).
Responses
Can also answer 400, 401 and 403, as described under Errors.
Get a deal
GET/api/v1/deals/{id}
Parameters
idstringin the pathrequiredThe record's id.
Responses
Can also answer 401 and 404, as described under Errors.
Update a deal
PATCH/api/v1/deals/{id}
Only the fields sent change. Sends the deal.updated webhook, and deal.won or deal.lost when the deal closes.
Parameters
idstringin the pathrequiredThe record's id.
Request body
titlestringvaluenumberstagestringOne of LEAD, QUALIFIED, PROPOSAL, NEGOTIATION, WON, LOST, or the key of a custom stage on the deal's board (400 otherwise).
probabilitynumber0 to 100, else 400. A stage change without a probability applies the new stage's default unless the deal's probability was set by hand (0 never counts as set by hand). WON is always 100 and LOST always 0, with or without a stage in the body.
notesstring, or nullcompanyIdstring, or nullcontactIdstring, or nullownerIdstring, or nullThe member who owns the deal (a user id of this workspace), or null for none. Sending the owner the deal already has is always accepted.
expectedClosedate, or nullThe date the deal is expected to close (2026-11-30, or a date and time, read as its date in the workspace's time zone).
closeReasonstring, or nullApplies to the stage the deal ends up in, which must be WON or LOST (else 400): with a stage change it is stored with the close; on a deal already closed it edits the stored reason (leave it out to keep it). Must be one of the workspace's reasons for that outcome, or "Other" with a closeNote. A stage change into WON or LOST without one stores no reason; reopening clears it. A body sent back from a GET works: a value equal to the deal's stored one is treated as unchanged, and null or "" is ignored on a deal that ends up open. Only a new value on an open deal is a 400. On a switch between WON and LOST the switch replaces the old close, so a repeated value is stored for the new outcome, except in a copy of the old close, which is ignored (the old outcome's reason and note are not carried over, and the request is not refused): a body sending both closeReason and closeNote equal to the stored pair, as a GET returns them (this includes "Other" and its note), or a stored reason not on the new outcome's list, with a closeNote equal to the stored one. To keep the same reason on the new outcome, send closeReason without closeNote, or with a new closeNote.
closeNotestring, or nullThe note with the close reason, under the same rules as closeReason.
Responses
Can also answer 400, 401, 403 and 404, as described under Errors.
Delete a deal
DELETE/api/v1/deals/{id}
Also deletes the files attached to the deal. A file that is also attached to another record stays on that record. Sends the deal.deleted webhook.
Parameters
idstringin the pathrequiredThe record's id.
Responses
200Resource deleted
dataobjectdata.idstringdata.deletedboolean
Can also answer 401, 403 and 404, as described under Errors.
Quotes
Customer-facing price quotes with line items and totals.
The quote object
idstringnumberstringorganizationIdstringstatusone of DRAFT, SENT, ACCEPTED, DECLINED, EXPIREDissueDatedate and timeexpiresAtdate and time, or nullcustomerNamestringcustomerEmailstring, or nullcustomerAddressstring, or nullcontactIdstring, or nullcontactobject, or nullThe contact's id, name and email, on a get and an update.
contact.idstringcontact.firstNamestringcontact.lastNamestring, or nullcontact.emailstring, or nulldealIdstring, or nulldealobject, or nullThe deal's id, title and stage, on a get and an update.
deal.idstringdeal.titlestringdeal.stageone of LEAD, QUALIFIED, PROPOSAL, NEGOTIATION, WON, LOSTcompanyIdstring, or nullThe company the quote was issued to, recorded when it was created: the companyId sent, else its contact's company, else its deal's. It does not follow the contact to another company later; it changes only when an update sends companyId, re-addresses the quote to another contactId, or (with no contact) moves it to another dealId. Company-wide customer portal access lists quotes by this field, read-only for anyone the quote wasn't sent to.
discountTypeone of flat, percent, or nulldiscountValuenumbertaxRatenumberPercent, 0 to 100.
subtotalnumbertotalnumbernotesstring, or nulltermsstring, or nullpaymentTermsstring, or nullread onlyThe "Payment Terms" label printed on the quote PDF when terms is blank or longer than one line, stamped from the workspace's due-in days when the quote is created ("Net 30") and never worked out again. null on quotes created before it existed.
sentAtdate and time, or nullviewedAtdate and time, or nullWhen the customer last opened the quote while it awaited a decision.
viewCountintegerHow many times the customer opened it (views within 30 minutes count once).
acceptedAtdate and time, or nulldeclinedAtdate and time, or nulldeclineReasonstring, or nullsignatureNamestring, or nullThe name the customer signed with.
signatureTitlestring, or nullrevisionOfIdstring, or nullThe quote this one revised.
revisedByobject, or nullThe newer revision that replaced this quote (its id and number), on a get and an update.
revisedBy.idstringrevisedBy.numberstringinvoiceIdstring, or nullThe invoice made from this quote, if one was.
lineItemsarray of line item objectscreatedAtdate and timeupdatedAtdate and time
The line item object
idstringnamestring, or nullA short product or SKU name.
descriptionstringquantitynumberunitPricenumberunitCostnumberYour cost, never shown to customers.
lineOfBusinessstring, or nullYour line of business for the line (such as "Microsoft" or "Managed Services"), which revenue by line of business reports on. null means not set.
totalnumberquantity times unitPrice.
positionintegerThe line's place on the document, from 0.
List quotes
GET/api/v1/quotes
Newest first, or oldest change first with updated_since. Each quote carries its line items.
Parameters
limitintegerqueryHow many records a page holds, from 1 to 200 (50 when left out).
cursorstringqueryThe nextCursor of the page before. With updated_since, only a cursor from a list read with updated_since works.
updated_sincedate and timequeryOnly records changed at or after this time, oldest change first (by updatedAt, then id). An ISO 8601 date and time with a time zone, such as 2026-10-01T00:00:00Z; anything else answers 400. Deleted records are not reported.
statusone of DRAFT, SENT, ACCEPTED, DECLINED, EXPIREDqueryOnly quotes with this status. Any other value is ignored.
Responses
Can also answer 400 and 401, as described under Errors.
Create a quote
POST/api/v1/quotes
The quote starts as a DRAFT and gets the workspace's next quote number. Totals are worked out from the lines, the discount and the tax. Sends the quote.created webhook and runs its automations.
Request body
customerNamestringrequiredcustomerEmailstringcustomerAddressstringcontactIdstringdealIdstringcompanyIdstringThe company (in this workspace; 400 otherwise) the quote is issued to. Left out, the contact's company, else the deal's.
expiresAtdateThe date the quote expires (2026-11-30, or a date and time, read as its date in the workspace's time zone).
discountTypeone of flat, percentdiscountValuenumberAn amount, or a percent up to 100 with percent.
taxRatenumberPercent, 0 to 100.
notesstringtermsstringlineItemsarray of line item input objectsrequired
Responses
Can also answer 400, 401 and 403, as described under Errors.
Get a quote
GET/api/v1/quotes/{id}
With its line items, its contact and deal, and the revision that replaced it, if any.
Parameters
idstringin the pathrequiredThe record's id.
Responses
Can also answer 401 and 404, as described under Errors.
Update a quote
PATCH/api/v1/quotes/{id}
Only the fields sent change. If lineItems is sent, it replaces every line. Totals (subtotal, total) are worked out again whenever lineItems, discountType, discountValue or taxRate change.
A quote the customer accepted or signed, or one replaced by a newer revision, can't be changed: the request answers 409. Revise it in the app instead.
Parameters
idstringin the pathrequiredThe record's id.
Request body
customerNamestringcustomerEmailstring, or nullcustomerAddressstring, or nullcontactIdstring, or nullRe-address the quote to another contact (the company it is issued to follows that contact's company unless companyId is also sent). null unlinks the contact and keeps the company.
dealIdstring, or nullcompanyIdstring, or nullThe company (in this workspace; 400 otherwise) the quote is issued to. null for none.
expiresAtdate, or nulldiscountTypeone of flat, percent, or nulldiscountValuenumbertaxRatenumbernotesstring, or nulltermsstring, or nulllineItemsarray of line item input objectsWhen sent, replaces every line.
Responses
Can also answer 400, 401, 403 and 404, as described under Errors.
Delete a quote
DELETE/api/v1/quotes/{id}
The app's rules apply. A quote the customer accepted or signed is deleted only with confirm set to its number. A version of a revised quote hands its place in the chain to the next version, and the newest version is deleted only while it is a draft that was never sent. Anything else answers 409, with an error that says why.
Parameters
Responses
Can also answer 401, 403 and 404, as described under Errors.
Change a quote's status
POST/api/v1/quotes/{id}/status
Does what the buttons on the quote page do. Moving a quote into ACCEPTED closes its deal as won (when it has one), makes its contact a customer if the contact was a lead, and sends the quote.accepted webhook. DECLINED stores the declineReason, if one is sent. Sending the status the quote already has changes nothing.
An accepted quote keeps its status, and a quote replaced by a newer revision is never sent or accepted: both answer 409.
Parameters
idstringin the pathrequiredThe record's id.
Request body
Responses
Can also answer 400, 401, 403 and 404, as described under Errors.
Invoices
Invoices, payment recording, and status transitions.
The invoice object
idstringnumberstringorganizationIdstringstatusone of DRAFT, SENT, PARTIALLY_PAID, PAID, OVERDUE, VOIDissueDatedate and timecustomerNamestringcustomerEmailstring, or nullcustomerAddressstring, or nullcontactIdstring, or nullcontactobject, or nullThe contact's id, name and email, on a get and an update.
contact.idstringcontact.firstNamestringcontact.lastNamestring, or nullcontact.emailstring, or nullcompanyIdstring, or nullThe company the invoice was issued to, recorded when it was created: the companyId sent, else its contact's company. It does not follow the contact to another company later; it changes only when an update sends companyId, or re-addresses the invoice to another contactId (setting contactId to null keeps it). Company-wide customer portal access lists invoices by this field.
dealIdstring, or nulldueDatedate and timediscountTypeone of flat, percent, or nulldiscountValuenumbertaxRatenumbersubtotalnumbertotalnumberamountPaidnumberpaidAtdate and time, or nullsentAtdate and time, or nullnotesstring, or nulltermsstring, or nullremindersEnabledbooleanWhether the customer gets payment reminders.
promisedPayDatedate and time, or nullThe date the customer promised to pay.
promiseNotestring, or nullportalUrlURL, or nullread onlyThe link to give the customer (added October 2026): the invoice's payment page, where they see the invoice and, when the workspace takes card payments through Stripe, pay it. Its Pay now charges the balance as it is when they click, so a link shared before a partial payment still charges the right amount. null for a DRAFT invoice (the page opens once the invoice is sent) and for a VOID one. Use this field wherever you send, show or store a customer's link to pay or view an invoice. Webhook deliveries that carry an invoice (invoice.created, invoice.paid) include it too.
stripePaymentLinkUrlstring, or nullread onlydeprecatedThe Stripe Payment Link BrytIO last created for this invoice (since October 2026, only when a customer chooses Pay now on its payment page), kept so existing integrations don't break. Don't give it to customers: it is usually null now (sending an invoice no longer creates one), and it is replaced when the balance changes. Use portalUrl instead.
lineItemsarray of line item objectspaymentsarray of payment objectscreatedAtdate and timeupdatedAtdate and time
The payment object
idstringinvoiceIdstringamountnumbermethodstringHow the payment was received. BrytIO writes manual, bank, check, stripe or other, and Marked paid on the record Mark paid makes (placeholder is then true). A payment recorded through the API keeps the method it was sent with, so expect other values too.
referencestring, or nullplaceholderbooleanTrue for the record Mark paid makes in the app, which is not money received.
paidAtdate and timecreatedAtdate and time
List invoices
GET/api/v1/invoices
Newest first, or oldest change first with updated_since. Each invoice carries its line items and payments.
Parameters
limitintegerqueryHow many records a page holds, from 1 to 200 (50 when left out).
cursorstringqueryThe nextCursor of the page before. With updated_since, only a cursor from a list read with updated_since works.
updated_sincedate and timequeryOnly records changed at or after this time, oldest change first (by updatedAt, then id). An ISO 8601 date and time with a time zone, such as 2026-10-01T00:00:00Z; anything else answers 400. Deleted records are not reported.
statusone of DRAFT, SENT, PARTIALLY_PAID, PAID, OVERDUE, VOIDqueryOnly invoices with this status. Any other value is ignored.
Responses
Can also answer 400 and 401, as described under Errors.
Create an invoice
POST/api/v1/invoices
The invoice starts as a DRAFT and gets the workspace's next invoice number. Totals are worked out from the lines, the discount and the tax. Sends the invoice.created webhook and runs its automations.
A line whose description starts with Prepayment received (invoice answers 400: that credit is added only by applying a paid invoice as a prepayment on the invoice page.
Request body
customerNamestringrequiredcustomerEmailstringcustomerAddressstringcontactIdstringcompanyIdstringThe company (in this workspace; 400 otherwise) the invoice is issued to: for billing a company with no contact, or naming the company outright. Left out, the contact's company.
dueDatedateThe date the invoice is due (2026-11-30, or a date and time, read as its date in the workspace's time zone). Left out, the window terms states ("Net 45" means 45 days from today), else the workspace's due-in days (Settings > General > Invoicing defaults; 30 unless changed).
discountTypeone of flat, percentdiscountValuenumberAn amount, or a percent up to 100 with percent.
taxRatenumberPercent, 0 to 100.
notesstringtermsstring, or nullLeft out, text matching the due date: the workspace's default terms text ("Net 30" unless changed) when due on the workspace's default window, else "Net N" for the window dueDate gives (or "Due on receipt"). Send "" for none.
remindersEnabledbooleanCustomer payment reminders. Defaults to the workspace's "reminders on for new invoices" setting.
lineItemsarray of line item input objectsrequired
Responses
Can also answer 400, 401 and 403, as described under Errors.
Get an invoice
GET/api/v1/invoices/{id}
With its line items, payments and contact.
Parameters
idstringin the pathrequiredThe record's id.
Responses
Can also answer 401 and 404, as described under Errors.
Update an invoice
PATCH/api/v1/invoices/{id}
Only the fields sent change. If lineItems is sent, it replaces every line, and the totals are worked out again.
Prepayments: send an applied Prepayment received (invoice ...) line back with any price and it is kept exactly as applied; leave it out and the prepayment is removed. A Prepayment received (invoice ...) line for a prepayment that isn't applied to this invoice answers 400. An invoice applied as a prepayment to another invoice keeps its total: lines that change it answer 409.
A request that changes the lines, the discount or the tax answers 409 and changes nothing if the invoice was changed by something else while it was being saved (a payment, a prepayment applied or removed): fetch it again and retry.
promisedPayDate and promiseNote log the customer's promise to pay, as Log promise does in the app: only on a sent invoice with a balance left.
Parameters
idstringin the pathrequiredThe record's id.
Request body
customerNamestringcustomerEmailstring, or nullcustomerAddressstring, or nullcontactIdstring, or nullRe-address the invoice to another contact (the company it is issued to follows that contact's company unless companyId is also sent). null unlinks the contact and keeps the company.
companyIdstring, or nullThe company (in this workspace; 400 otherwise) the invoice is issued to. null for none.
dueDatedate, or nullnotesstring, or nulltermsstring, or nullremindersEnabledbooleanSwitch this invoice's customer payment reminders on or off.
promisedPayDatedate, or nullThe date the customer promised to pay (2026-11-30), on a sent invoice with a balance left (400 otherwise). null clears the promise and its note.
promiseNotestring, or nullA note with the promise. Needs a promisedPayDate.
discountTypeone of flat, percent, or nulldiscountValuenumbertaxRatenumberlineItemsarray of line item input objectsWhen sent, replaces every line.
Responses
Can also answer 400, 401, 403 and 404, as described under Errors.
Delete a draft invoice
DELETE/api/v1/invoices/{id}
Deletes a draft invoice (and its line items) that was never sent, never paid and never given a Stripe pay link.
Contract change (October 2026): an invoice that was sent (it has a sentAt, or its status is SENT, OVERDUE, PARTIALLY_PAID or PAID) or that was ever paid (it has a payment, or a paidAt: an invoice marked paid and then moved back to DRAFT or to VOID still counts) is no longer deleted, together with its payments. Neither is one that was given a Stripe pay link (until October 2026 the app's Create pay link worked on a draft), since the customer may already have that link. The request answers 409 with an error that says to void the invoice instead: POST /invoices/{id}/status with {"status": "VOID"}, which keeps the invoice and any payments received. A mark-paid record (a payment with placeholder: true) is not money received: voiding releases it, as moving the invoice to any status other than PAID does. A draft that was voided without ever being sent, paid or given a pay link can still be deleted. The in-app bulk delete follows the same rule.
Parameters
idstringin the pathrequiredThe record's id.
Responses
200Resource deleted
dataobjectdata.idstringdata.deletedboolean
409The invoice was sent, paid or given a pay link, so it is kept. Void it instead. An invoice that is already void is kept as it is, and its error says so instead of asking you to void it.
Can also answer 401, 403 and 404, as described under Errors.
Change an invoice's status
POST/api/v1/invoices/{id}/status
Sends the invoice.paid webhook when the invoice moves into PAID.
A VOID invoice takes no payment: setting it to PAID or PARTIALLY_PAID answers 409 and changes nothing. Contract change (October 2026): before, PAID on a void invoice answered 400 and changed nothing, and PARTIALLY_PAID answered 200 and was applied, moving the void invoice to PARTIALLY_PAID. Both now answer 409. Setting a void invoice back to DRAFT, SENT or OVERDUE is still allowed, unless it still carries a Prepayment received (invoice ...) line: voiding it released that prepayment, so the move answers 409 and changes nothing until the line is deleted.
An invoice applied as a prepayment to another invoice keeps its status: any change answers 409 until the prepayment is removed from that invoice.
Parameters
idstringin the pathrequiredThe record's id.
Request body
statusone of DRAFT, SENT, PARTIALLY_PAID, PAID, OVERDUE, VOIDrequired
Responses
200The invoice objectStatus changed (or already that status)
409The invoice is VOID, so it takes no payment. This request recorded and changed nothing. Set it back to SENT first if it really is owed.
Can also answer 400, 401, 403 and 404, as described under Errors.
List an invoice's payments
GET/api/v1/invoices/{id}/payments
Newest payment first.
Parameters
idstringin the pathrequiredThe record's id.
Responses
Can also answer 401 and 404, as described under Errors.
Record a payment on an invoice
POST/api/v1/invoices/{id}/payments
Moves the invoice to PARTIALLY_PAID or PAID by what has been paid in all. Sends the invoice.paid webhook when it is paid in full.
Contract change (October 2026): a VOID invoice takes no payment. The request answers 409 and records nothing; before, the payment was recorded and moved the voided invoice to PARTIALLY_PAID or PAID.
reference makes a retry safe: a payment with a reference already recorded on this invoice is not recorded again, and the request answers 200 with duplicate: true. That holds after the invoice is voided too, so a retry of a payment recorded before the void (its first response lost) answers 200, never 409: the money is in BrytIO. A 409 means this request recorded nothing.
Parameters
idstringin the pathrequiredThe record's id.
Request body
amountnumberrequiredThe amount received, in the workspace's currency. Must be more than 0.
methodstringHow the payment was received, kept as sent. BrytIO itself uses manual, bank, check, stripe and other.
referencestring, or nullA check number, wire reference or Stripe charge id. MARK_PAID is reserved.
Responses
200Duplicate: a payment with this reference is already recorded on this invoice (a retry), so nothing new was recorded. Answered whatever the invoice's status now is, VOID included.
201Payment recorded; invoice status updated
409The invoice is VOID, so it takes no payment. This request recorded and changed nothing. Set it back to SENT first if it really is owed.
Can also answer 400, 401, 403 and 404, as described under Errors.
Activities
Notes, calls, emails, meetings, and tasks linked to CRM records.
The activity object
idstringorganizationIdstringtypeone of NOTE, CALL, EMAIL, MEETING, TASK, SYSTEMtitlestringbodystring, or nulldueAtdate and time, or nullcompletedAtdate and time, or nullrepeatRuleone of daily, weekly, monthly, or nullTasks only. Completing the task creates its next occurrence (same links and assignee, stepping from repeatAnchorAt at the same time of day).
repeatNextIdstring, or nullId of the occurrence created when this task was completed.
repeatAnchorAtdate and time, or nullThe series anchor every occurrence steps from (its first dueAt; moved when dueAt is changed through the API or the app, never by a snooze). Server-maintained.
repeatSeriesIdstring, or nullId of the series' first task, on every occurrence it spawned (null on the first task). Server-maintained.
priorityone of high, or nullTasks only. "high", or null for normal priority.
emailReminderbooleanTasks only. When true, the assignee (while still a member) is also emailed when the task comes due.
callOutcomestring, or nullCalls and meetings only. A CALL: connected, voicemail, no_answer, busy, wrong_number. A MEETING: held, no_show, rescheduled, cancelled.
durationMinutesinteger, or nullCalls and meetings only. Length in whole minutes.
userIdstring, or nullThe member who logged it (a user id). null for one created through the API.
assignedToIdstring, or nullThe member a task is assigned to (a user id).
contactIdstring, or nulldealIdstring, or nullcompanyIdstring, or nullsourcestringWhere it came from. app for one made in BrytIO or through the API; microsoft, google or bcc for an email a connected mailbox or the BCC address logged.
directionstring, or nullA logged email's direction, IN or OUT. null on other activities.
occurredAtdate and time, or nullWhen it happened (a logged email's sent or received time); set to the creation time otherwise.
createdAtdate and timeupdatedAtdate and timeThe last change. Activities created before October 2026 show the time this field was added.
List activities
GET/api/v1/activities
Newest first, or oldest change first with updated_since. A synced email that was deleted (hidden) is not listed.
Parameters
limitintegerqueryHow many records a page holds, from 1 to 200 (50 when left out).
cursorstringqueryThe nextCursor of the page before. With updated_since, only a cursor from a list read with updated_since works.
updated_sincedate and timequeryOnly records changed at or after this time, oldest change first (by updatedAt, then id). An ISO 8601 date and time with a time zone, such as 2026-10-01T00:00:00Z; anything else answers 400. Deleted records are not reported.
contactIdstringqueryOnly this contact's activities.
dealIdstringqueryOnly this deal's activities.
companyIdstringqueryOnly this company's activities.
typeone of NOTE, CALL, EMAIL, MEETING, TASK, SYSTEMqueryOnly activities of this type. Any other value is ignored.
Responses
Can also answer 400 and 401, as described under Errors.
Create an activity
POST/api/v1/activities
A note, call, email, meeting or task. Every activity must be linked to at least one of contactId, dealId or companyId.
Request body
At least one of contactId, dealId or companyId is required.
typeone of NOTE, CALL, EMAIL, MEETING, TASK, SYSTEMtitlestringrequiredbodystring, or nulldueAtdate and timecompletedAtdate and timerepeatRuleone of daily, weekly, monthlyTasks only; needs dueAt.
priorityone of high, normal, or nullTasks only. "high" flags the task; "normal" or null leaves it unflagged.
emailReminderbooleanTasks only. Email the assignee when the task comes due (default false).
callOutcomestring, or nullCalls and meetings only (400 otherwise). A CALL: connected, voicemail, no_answer, busy, wrong_number. A MEETING: held, no_show, rescheduled, cancelled.
durationMinutesinteger, or nullCalls and meetings only (400 otherwise). Length in whole minutes.
contactIdstringdealIdstringcompanyIdstring
Responses
Can also answer 400, 401 and 403, as described under Errors.
Get an activity
GET/api/v1/activities/{id}
Parameters
idstringin the pathrequiredThe record's id.
Responses
Can also answer 401 and 404, as described under Errors.
Update an activity
PATCH/api/v1/activities/{id}
Only the fields sent change. Completing a repeating task creates its next occurrence. An email logged by a connected mailbox or the BCC address can't be edited: the request answers 409.
Parameters
idstringin the pathrequiredThe record's id.
Request body
typeone of NOTE, CALL, EMAIL, MEETING, TASK, SYSTEMtitlestringbodystring, or nulldueAtdate and time, or nullcompletedAtdate and time, or nullrepeatRuleone of daily, weekly, monthly, or nullTasks only; needs a due date. null stops the repeat.
priorityone of high, normal, or nullTasks only. "high" flags the task; "normal" or null clears the flag.
emailReminderbooleanTasks only. Email the assignee when the task comes due.
callOutcomestring, or nullCalls and meetings only, judged on the type the activity will have (see ActivityCreate). Turning the activity into another type clears it, and a call turned into a meeting drops an outcome a meeting doesn't have.
durationMinutesinteger, or nullCalls and meetings only. Cleared when the activity becomes another type.
contactIdstring, or nulldealIdstring, or nullcompanyIdstring, or null
Responses
Can also answer 400, 401, 403 and 404, as described under Errors.
Delete an activity
DELETE/api/v1/activities/{id}
An email logged by a connected mailbox or the BCC address (source microsoft, google or bcc) is hidden rather than deleted, so the next mailbox sync doesn't write it back; the response then carries hidden: true, and the activity answers 404 and leaves every list from then on. The message itself stays in the mailbox. Such an email can't be edited (PATCH answers 409). A synced message is logged once per matched contact, each copy its own activity with the same Message-ID. By default (scope=message) deleting one hides every contact's copy; scope=activity hides only this one. hiddenIds lists every activity hidden, this one first. Deleting a call, meeting or email works out its contact's lastContactedAt again from the ones that remain.
Parameters
Responses
Can also answer 400, 401, 403 and 404, as described under Errors.
Sequences
Email sequences and their enrollments.
The sequence object
idstringorganizationIdstringnamestringdescriptionstring, or nullenabledbooleanWhether it sends. A sequence that is off sends nothing and enrolls no one.
stepsstringThe emails, as a JSON array in a string: each step has an id, a delayDays (days after the step before; 0 sends right away), a subject and a body, which can use merge fields such as {{firstName}}.
_countobjectOn a list and a get.
_count.enrollmentsintegerEvery enrollment the sequence has had.
enrollmentsarray of objectsOn the list only. One active enrollment's id when it has any, so an empty array means no one is enrolled now.
enrollments[].idstringcreatedAtdate and timeupdatedAtdate and time
List email sequences
GET/api/v1/sequences
Every sequence in one response, newest first, or only the ones changed at or after updated_since, oldest change first.
Parameters
updated_sincedate and timequeryOnly records changed at or after this time, oldest change first (by updatedAt, then id). An ISO 8601 date and time with a time zone, such as 2026-10-01T00:00:00Z; anything else answers 400. Deleted records are not reported.
Responses
Can also answer 400 and 401, as described under Errors.
Create an email sequence
POST/api/v1/sequences
The plan must include email sequences (403 otherwise).
Request body
namestringrequireddescriptionstring, or nullenabledbooleanstepsarray of objectsrequiredThe emails, in order (an array, or the same array as a JSON string). Each step: delayDays (a number of days after the step before), subject and body.
steps[].idstringsteps[].delayDaysnumbersteps[].subjectstringsteps[].bodystring
Responses
Can also answer 400, 401 and 403, as described under Errors.
Get an email sequence
GET/api/v1/sequences/{id}
Parameters
idstringin the pathrequiredThe record's id.
Responses
Can also answer 401 and 404, as described under Errors.
Update an email sequence
PATCH/api/v1/sequences/{id}
Only the fields sent change. Turning a sequence back on needs a plan that includes sequences; turning it off and editing it don't.
Parameters
idstringin the pathrequiredThe record's id.
Request body
Responses
Can also answer 400, 401, 403 and 404, as described under Errors.
Delete an email sequence
DELETE/api/v1/sequences/{id}
Its enrollments are deleted with it, so nothing more is sent. Answers 204 with no body.
Parameters
idstringin the pathrequiredThe record's id.
Responses
Can also answer 401, 403 and 404, as described under Errors.
Enroll contacts in an email sequence
POST/api/v1/sequences/{id}/enroll
Enrolls each contact once, at its email address. A contact without an email, one already enrolled, or an id that isn't a contact of this workspace is skipped, and so is every contact when the sequence is off or not this workspace's. The plan must include email sequences.
Parameters
idstringin the pathrequiredThe record's id.
Request body
Responses
Can also answer 400, 401 and 403, as described under Errors.
Automations
Automations that run steps when something happens in BrytIO.
The automation object
idstringorganizationIdstringnamestringdescriptionstring, or nullenabledbooleantriggerstringWhat starts it, as JSON in a string: the event (such as deal.won) and any conditions on the record.
actionsstringThe steps it runs, as a JSON array in a string.
_countobjectOn a list and a get.
_count.runsintegerHow many times it has run.
createdAtdate and timeupdatedAtdate and time
List automations
GET/api/v1/automations
Every automation in one response, newest first, or only the ones changed at or after updated_since, oldest change first.
Parameters
updated_sincedate and timequeryOnly records changed at or after this time, oldest change first (by updatedAt, then id). An ISO 8601 date and time with a time zone, such as 2026-10-01T00:00:00Z; anything else answers 400. Deleted records are not reported.
Responses
Can also answer 400 and 401, as described under Errors.
Create an automation
POST/api/v1/automations
The same rules as the automation builder in the app. The plan must include automations (403 otherwise).
Request body
namestringrequireddescriptionstring, or nullenabledbooleantriggerobjectrequiredWhat starts it (an object, or the same object as a JSON string): {"event": "deal.won"}, with conditions on the record's fields if you want them. Build one in the app and read it back to see the full shape.
actionsarray of objectsrequiredThe steps to run, in order (an array, or the same array as a JSON string). Each has a type, such as send_notification, and that step's settings.
Responses
Can also answer 400, 401 and 403, as described under Errors.
Get an automation
GET/api/v1/automations/{id}
Parameters
idstringin the pathrequiredThe record's id.
Responses
Can also answer 401 and 404, as described under Errors.
Update an automation
PATCH/api/v1/automations/{id}
Only the fields sent change. A new trigger or new steps are checked against each other, as the builder checks them. Turning an automation back on needs a plan that includes automations; turning it off and editing it don't.
Parameters
idstringin the pathrequiredThe record's id.
Request body
namestringdescriptionstring, or nullenabledbooleantriggerobjectA new trigger (an object, or the same object as a JSON string).
actionsarray of objectsNew steps, replacing every step (an array, or the same array as a JSON string).
Responses
Can also answer 400, 401, 403 and 404, as described under Errors.
Delete an automation
DELETE/api/v1/automations/{id}
Its run history goes with it.
Parameters
idstringin the pathrequiredThe record's id.
Responses
200Resource deleted
dataobjectdata.idstringdata.deletedboolean
Can also answer 401, 403 and 404, as described under Errors.
Shipments
Shipment tracking with status events.
The shipment object
idstringorganizationIdstringreferencestringYour own order reference.
trackingNumberstringcarrierstringstatusone of PENDING, LABEL_CREATED, IN_TRANSIT, OUT_FOR_DELIVERY, DELIVERED, EXCEPTION, RETURNED, CANCELLEDcustomerNamestringcustomerEmailstring, or nullcontactIdstring, or nullThe contact the shipment is tracked against.
dealIdstring, or nullThe deal the shipment is tracked against. Set to null when the deal is deleted.
invoiceIdstring, or nullThe invoice the shipment is tracked against. Set to null when the invoice is deleted.
fromAddressstring, or nulltoAddressstring, or nullestimatedDeliverydate and time, or nullactualDeliverydate and time, or nulleventsarray of objectsTracking events, newest first (the 5 latest on a list).
events[].idstringevents[].statusone of PENDING, LABEL_CREATED, IN_TRANSIT, OUT_FOR_DELIVERY, DELIVERED, EXCEPTION, RETURNED, CANCELLEDevents[].descriptionstringevents[].locationstring, or nullevents[].occurredAtdate and timecreatedAtdate and timeupdatedAtdate and time
List shipments
GET/api/v1/shipments
Newest first, or oldest change first with updated_since. Each shipment carries its 5 latest tracking events.
Parameters
limitintegerqueryHow many records a page holds, from 1 to 200 (50 when left out).
cursorstringqueryThe nextCursor of the page before. With updated_since, only a cursor from a list read with updated_since works.
updated_sincedate and timequeryOnly records changed at or after this time, oldest change first (by updatedAt, then id). An ISO 8601 date and time with a time zone, such as 2026-10-01T00:00:00Z; anything else answers 400. Deleted records are not reported.
statusone of PENDING, LABEL_CREATED, IN_TRANSIT, OUT_FOR_DELIVERY, DELIVERED, EXCEPTION, RETURNED, CANCELLEDqueryOnly shipments with this status. Any other value is ignored.
Responses
Can also answer 400 and 401, as described under Errors.
Create a shipment
POST/api/v1/shipments
BrytIO then tracks it with the carrier and updates its status. Runs the shipment.created automations.
Request body
customerName is required unless invoiceId is set: then a blank customerName or customerEmail is filled from the invoice's customer. contactId, dealId and invoiceId must be records of your workspace, or the request is refused with 400 and nothing is created.
referencestringrequiredtrackingNumberstringrequiredcarrierstringrequiredcustomerNamestringcustomerEmailstringfromAddressstringtoAddressstringcontactIdstring, or nulldealIdstring, or nullinvoiceIdstring, or null
Responses
Can also answer 400, 401 and 403, as described under Errors.
Get a shipment
GET/api/v1/shipments/{id}
With all its tracking events, newest first.
Parameters
idstringin the pathrequiredThe record's id.
Responses
Can also answer 401 and 404, as described under Errors.
Update a shipment
PATCH/api/v1/shipments/{id}
Only the fields sent change. Setting status to DELIVERED also sets actualDelivery to now.
Parameters
idstringin the pathrequiredThe record's id.
Request body
Only the fields sent change. Send contactId, dealId or invoiceId as an id to link it, or null to unlink it. Each id must be a record of your workspace, or the request is refused with 400 and nothing changes. Linking an invoice fills a blank customerName or customerEmail from the invoice's customer.
statusone of PENDING, LABEL_CREATED, IN_TRANSIT, OUT_FOR_DELIVERY, DELIVERED, EXCEPTION, RETURNED, CANCELLEDfromAddressstring, or nulltoAddressstring, or nullestimatedDeliverydate and time, or nullcontactIdstring, or nulldealIdstring, or nullinvoiceIdstring, or null
Responses
Can also answer 400, 401, 403 and 404, as described under Errors.
Delete a shipment
DELETE/api/v1/shipments/{id}
Parameters
idstringin the pathrequiredThe record's id.
Responses
200Resource deleted
dataobjectdata.idstringdata.deletedboolean
Can also answer 401, 403 and 404, as described under Errors.