Calimatic Connect|Documentation
PricingSign In

Records

Records are provider-delivered resources — contacts, deals, orders, charges, messages, events, tickets — stored in one unified place. Everything your connected providers send you, across every resource type, lands in the records store automatically.

Why Records exist

The platform started Meta Lead Ads → CRM lead sync. The leads table is still the home of that original pipeline. But real integration work touches every resource type a provider exposes: HubSpot contacts, Stripe charges, Shopify orders, Zendesk tickets, Slack messages, Google Calendar events, Twilio SMS, Zoom recordings. Records gives each of these a first-class home without requiring a per-type admin UI.

The model

Every record has:

  • provider_type — which adapter delivered it (hubspot, stripe, …)
  • resource_type — what kind of thing it is (contact, deal, charge, …)
  • external_id — the provider's native id, for idempotent re-ingestion
  • source — how it arrived: webhook, poll, import, or flow_step
  • data — the JSONB payload, free-form and provider-specific

The unique index on (account, provider_type, resource_type, external_id) makes ingestion idempotent: the same provider record can't be stored twice, and re-delivery merges updates into the existing row.

The three sources

Webhooks

When a webhook adapter opts in by setting recordResourceType in its parseWebhookPayload(), every incoming event mirrors into records automatically. Currently opted in: Meta Lead Ads, HubSpot (contact/deal/company/ticket), Google Calendar, Twilio, Zoom, Slack, WhatsApp.

Poll triggers

Poll-kind flow trigger subscriptions can specify a target resource type in their config, and the scheduler ingests every polled item before firing the flow:

{
  "kind": "poll",
  "providerType": "hubspot",
  "triggerName": "contact_list",
  "config": {
    "pollAction": "list_contacts",
    "cursorPath": "results.0.updatedAt",
    "recordResourceType": "contact",
    "recordItemsPath": "results",
    "recordExternalIdPath": "id"
  }
}

CSV import

Use /admin/records/import to bulk-upload records from a CSV. Pick a provider type (use manual for hand-curated data) and a resource type, paste your CSV, and submit. An external_id or id column makes re-imports merge into existing rows.

Browsing records

/admin/records is the unified browser. The resource-type pills at the top let you pivot instantly between contacts, deals, messages, events, etc. The free-text search scans the JSONB data across every field. Click any row to open the detail view with the full pretty-printed payload.

Re-running flows against a record

The detail page has a Re-run flows button that calls POST /connect/v1/records/:id/replay. This re-dispatches the record through every active flow subscribed to (providerType, resourceType) — useful after fixing a broken flow step, or when a flow was added after the original event. The response reports how many flows matched and how many ran.

Records ↔ Tables crosslink

The record.to_table flow step copies a record into a user Table via upsert. Use it to deduplicate records across providers, build a canonical customers view, or stage records for flows that read from tables instead of webhooks.

{
  "kind": "record.to_table",
  "config": {
    "tableSlug": "customers",
    "keyField": "email"
  },
  "inputMapping": {
    "recordId": "{{trigger.recordId}}"
  }
}

API reference

GET    /connect/v1/records                             # records.view
    ?resourceType=<type>&providerType=<provider>&search=<text>&page=<n>
GET    /connect/v1/records/summary                     # records.view
GET    /connect/v1/records/:id                         # records.view
DELETE /connect/v1/records/:id                         # records.write
POST   /connect/v1/records/:id/replay                  # records.write
POST   /connect/v1/records/import                      # records.write
     { "providerType": "...", "resourceType": "...", "csv": "..." }

Relationship to Leads

The leads table and the /admin/leads page are still live and unchanged. Lead webhooks continue to flow through the legacy pipeline (upsert into leads → CRM sync → flow dispatch), and those leads also mirror into records with resource_type='lead' as a side effect.

The endgame — tracked in the seed at .planning/seeds/generic-records-model.md — is for the Leads page to become a saved view over records. That refactor is a separate milestone because it touches the legacy CRM sync path.