Last updated on October 23, 2026
CRM data
Read an end customer's contacts, companies and deals in one shape, whichever CRM they use.
What crm does
crm.contacts, crm.companies and crm.deals read an end customer's CRM in one shape, whichever CRM they use. Each has list and get.
const page = await appnigma.crm.contacts.list({ endCustomerId: "lawfirm-123", platform: "attio", limit: 50 });
for (const contact of page.records) console.log(contact.fullName, contact.emails[0]?.value);
// The next page, while there is one.
if (page.next) {
await appnigma.crm.contacts.list({ endCustomerId: "lawfirm-123", platform: "attio", cursor: page.next });
}
const { record } = await appnigma.crm.deals.get({ endCustomerId: "lawfirm-123", platform: "hubspot", id: "1234" });
console.log(record.name, record.amount, record.currency, record.status);page = client.crm.contacts.list(end_customer_id: "lawfirm-123", platform: "attio", limit: 50)
page[:records].each { |contact| puts "#{contact[:full_name]} #{contact[:emails].first&.dig(:value)}" }
# The next page, while there is one.
client.crm.contacts.list(end_customer_id: "lawfirm-123", platform: "attio", cursor: page[:next]) if page[:next]
deal = client.crm.deals.get(end_customer_id: "lawfirm-123", platform: "hubspot", id: "1234")[:record]
puts "#{deal[:name]} #{deal[:amount]} #{deal[:currency]} #{deal[:status]}"page = client.crm.contacts.list(end_customer_id="lawfirm-123", platform="attio", limit=50)
for contact in page["records"]:
email = contact["emails"][0]["value"] if contact["emails"] else None
print(contact["full_name"], email)
# The next page, while there is one.
if page["next"]:
client.crm.contacts.list(end_customer_id="lawfirm-123", platform="attio", cursor=page["next"])
deal = client.crm.deals.get(end_customer_id="lawfirm-123", platform="hubspot", id="1234")["record"]
print(deal["name"], deal["amount"], deal["currency"], deal["status"])Read a page: list
Names are shown as in TypeScript. Ruby and Python use the same names in snake case: end_customer_id.
Your own id for your customer. 1 to 100 characters of letters, digits, dot, dash or underscore.
The platform, such as hubspot or attio. It must be switched on for your company.
How many records in one page, from 1 to 100.
The next value from the previous page. Leave it out for the first page.
The records in this page, in the shapes below.
Pass it as cursor for the next page. Empty on the last page.
Read one record: get
Your own id for your customer.
The platform, such as hubspot.
The record's id in the CRM.
The record, in the shape below.
The shapes
Every field is always there: a value the CRM does not have is empty (null in TypeScript, nil in Ruby, None in Python), never left out.
Field names are shown as in TypeScript. In Ruby and Python they are the same in snake case: full_name, created_at, custom_fields, and so on.
| Object | Fields |
|---|---|
| Every record | id, platform, createdAt, updatedAt, customFields, and raw, the record exactly as the CRM sent it |
| Contact | firstName, lastName, fullName, emails, phones (the primary one first), companyId, ownerId |
| Company | name, domain, ownerId |
| Deal | name, amount, currency, status (open, won or lost), stage, pipeline, probability, closeDate, closedAt, companyId, ownerId |
Some fields hold more than one value:
| Field | Shape |
|---|---|
emails | A list. Each has value, type (work, personal, other or empty) and primary (true or false). |
phones | A list. Each has value, type (work, mobile, home, fax, other or empty) and primary. |
stage, pipeline | The CRM's own stage or pipeline: its id, and its label when the CRM has one. |
customFields | The CRM's own fields, keyed by the CRM's field names. Each has a label and a value. |
When it fails
DATA_TYPE_NOT_AVAILABLE: the platform has no unified data yet for that object. Reach it with request instead. In TypeScript this is aSdkNotAvailableError, in Ruby anAppnigma::NotAvailableError, in Python aNotAvailableError.RECORD_NOT_FOUND: no record with that id. In TypeScript this is aSdkNotFoundError, in Ruby anAppnigma::NotFoundError, in Python aNotFoundError.
Reads are tried again only when that is safe, up to three times by default. See Retries and time limits. Every code is on Errors.