salesintermediate10 min setupby Mantle

New Airtable record to a HubSpot contact

A record added to an Airtable base becomes a HubSpot contact, so the list people keep by hand reaches the CRM.

Record addedCreate the conta

Drag to pan, pinch to zoom. This is the real graph the template installs.

Purpose

A record added to an Airtable base becomes a HubSpot contact, so the list people keep by hand reaches the CRM.

How it works

What it does

  • Watches one Airtable table for records being added.
  • Creates a HubSpot contact from the record's fields.

Validation

This template is contract-validated: the unit suite checks every node type, every config key and the whole graph against the same rules a saved workflow passes. It has not been run against the live provider — the ids, names and columns below are placeholders to replace with your own.

Nodes 2

  1. 01Record addedtrigger.airtable_recordTrigger
  2. 02Create the contacthubspot.create_contactApp
View template JSON
{
  "format": "mantle-workflow/v1",
  "id": "airtable-record-to-hubspot-contact",
  "name": "New Airtable record to a HubSpot contact",
  "graph": {
    "nodes": [
      {
        "id": "change",
        "type": "trigger.airtable_record",
        "label": "Record added",
        "config": {
          "base_id": "appXXXXXXXXXXXXXX",
          "table_id": "tblXXXXXXXXXXXXXX",
          "change_types": [
            "add"
          ]
        },
        "worker_type": "generic-worker",
        "position": {
          "x": 60,
          "y": 220
        }
      },
      {
        "id": "contact",
        "type": "hubspot.create_contact",
        "label": "Create the contact",
        "config": {
          "email": "{{ $json.fields.Email }}",
          "firstname": "{{ $json.fields[\"First Name\"] }}",
          "lastname": "{{ $json.fields[\"Last Name\"] }}",
          "properties": {
            "company": "{{ $json.fields.Company }}"
          }
        },
        "worker_type": "generic-worker",
        "position": {
          "x": 380,
          "y": 220
        }
      }
    ],
    "edges": [
      {
        "from": "change",
        "to": "contact"
      }
    ]
  }
}

Requirements

The connections and permissions each step needs, from the node contracts in the catalog.

  • AirtableBearer token

    Used by Record added.

    Credential: the Airtable personal access token

    • The token needs the webhook:manage scope as well as data read access to the base — webhook:manage creates the subscription and reads its change log. Publishing also needs MANTLE_APP_PUBLIC_BASE_URL set: it is the address Airtable calls back on.
    • Only changes made after the workflow is published fire it — the webhook's change log starts when the subscription is created, so nothing is replayed.
    • One run per changed record, idempotent on Airtable's own transaction number, so a re-read of the change log cannot start a second run.
    • Deliveries are verified against Airtable's MAC secret before any run starts; an unsigned or mis-signed POST is refused with 401.
    • The subscription expires after 7 days and is refreshed automatically while the workflow is published; it is deleted at Airtable when the workflow is unpublished, disabled or archived.
  • HubSpotBearer token

    Used by Create the contact.

    Credential: the HubSpot private-app token

Setup

  • base_id starts app… and table_id starts tbl…; both are in the table's URL.
  • The Airtable token needs webhook:manage as well as data read access, and MANTLE_APP_PUBLIC_BASE_URL must be set — it is the address Airtable calls back on.
  • The expressions read your column names (Email, First Name, …). Change them to match; a column name with a space needs bracket form, {{ $json.fields["First Name"] }}.

Sample input and output

Synthetic examples: node-contract samples or placeholders, never customer data.

Input

What the Record added trigger receives, as its node contract samples it.

{
  "base_id": "appXXXXXXXXXXXXXX",
  "change_type": "update",
  "cursor": 0,
  "fields": {},
  "payload": {},
  "previous_fields": {},
  "record_id": "recXXXXXXXXXXXXXX",
  "source": "client",
  "table_id": "tblXXXXXXXXXXXXXX",
  "timestamp": "",
  "transaction_number": 0
}

Output

Create the contact hubspot.create_contact

{
  "contact_id": "",
  "ok": false
}

Failure handling

What the graph does when a step fails, read from its retry settings, error branches and error policies.

  • A step that fails, after any retries, fails the run. The failed step is kept with its input and error.

See it run, then make it yours.

Templates install as drafts. Edit any step, and Workflow Health re-checks the graph on every save.