Skip to content

Airtable x GREEN-API - develop your integration#

This integration lets you send WhatsApp, Telegram and MAX messages from an Airtable base and receive replies back into it through the GREEN-API platform. Built on the Universal Integration Platform by GREEN-API, it consists of two parts kept in the same repository: the Airtable extension (extension/) and the adapter service (src/).

Table of contents#

Overview#

This documentation guides you through deploying your own copy of the integration between GREEN-API and Airtable. You will:

  1. Deploy the adapter service on a public HTTPS domain with a MySQL database.
  2. Register your own Airtable OAuth integration with the redirect URL of that domain and put its client id and client secret into .env.
  3. Create a custom extension in your Airtable base and connect the extension/ project to it.
  4. Open the extension, set your adapter URL in Settings → Advanced, confirm your identity and add instances.

Working with the integration after installation - confirming identity, collaborator permissions by role, sending and receiving messages - is described in Connecting the integration. This page covers only what you need to deploy and extend your own copy.

Architecture#

Airtable base ──(extension UI)──▶ adapter ──▶ GREEN-API ──▶ WhatsApp / Telegram / MAX
      ▲                              │
      └──(Airtable Web API, OAuth)───┘◀── webhooks (messages, statuses) ◀── GREEN-API

Airtable extension#

The Blocks SDK extension that:

  • Holds the base settings: table and field mapping, sending settings
  • Builds a message for each row and shows a preview
  • Hands the batch of messages to the adapter and shows the result for each row
  • Keeps the collaborator's personal key in the browser's local storage

Adapter service#

The NestJS application that:

  • Keeps GREEN-API instance credentials. Nothing secret is stored in the Airtable base
  • Confirms collaborators' identity through Airtable OAuth, checks their role in the base and issues personal keys
  • Sends messages through GREEN-API and reports the result for each row
  • Receives GREEN-API webhooks and writes incoming and outgoing messages and statuses into the base through the Airtable Web API

Installation#

Prerequisites#

  • Node.js 24
  • MySQL 8 (or MariaDB)
  • A public HTTPS URL for the adapter - GREEN-API webhooks and OAuth callbacks must reach it
  • A GREEN-API account and instance(s)
  • A paid Airtable plan - custom extensions and the Marketplace are available on paid plans only

Registering the Airtable OAuth integration#

At airtable.com/create/oauth:

  1. Name and register the integration.

  2. Generate a client secret (Generate a client secret). The token exchange is done by the adapter, which is a server, so Airtable requires it. Copy it into .env.

  3. Set the OAuth redirect URL: <APP_URL>/api/v1/oauth/callback.

  4. Select scopes - exactly these four:

    Scope Used for
    user.email:read identifying the collaborator when they confirm identity
    schema.bases:read reading their role in the base, and the table and field names for receiving
    data.records:read finding the contact to link an incoming message to
    data.records:write writing message rows and statuses

PKCE is always used (Airtable requires it). The client secret is sent as HTTP Basic auth on the token and refresh requests. Access tokens live 60 minutes and are refreshed by the adapter. Refresh tokens live 60 days and are renewed whenever used.

Setting up the adapter#

1. Clone the repository:

```bash
git clone https://github.com/green-api/greenapi-integration-airtable.git
cd greenapi-integration-airtable
```
  1. Install dependencies:

    npm ci
    
  2. Configure environment variables:

    cp .env.example .env
    

    Fill in the following variables in .env:

    Variable Meaning
    DATABASE_URL MySQL connection string, e.g. mysql://user:pass@host:3306/adapter
    APP_URL public base URL of the adapter, e.g. https://your-adapter-domain.com. Used to build the OAuth redirect (/api/v1/oauth/callback) and the webhook URL (/api/v1/webhooks/green-api)
    PORT listen port, default 3000
    AIRTABLE_CLIENT_ID client id of the Airtable OAuth integration (step above)
    AIRTABLE_CLIENT_SECRET client secret of the same integration. Required: the adapter refuses to start without it

    All variables are validated at startup.

  3. Apply database migrations:

    npx prisma migrate deploy
    
  4. Build and start the adapter:

    # Build the application
    npm run build
    
    # Start in production mode
    npm run start:prod
    

Deployment using Docker#

The repository root contains a Dockerfile and a docker-compose.yml. To start, run:

docker compose up --build

The command runs database migrations and then starts the adapter.

Running and releasing the extension#

extension/ is a standard Blocks SDK project that is already initialised. Create the extension in the base and run it in development mode by following Install the extension in the base. While developing, keep in mind:

  • extension/.block/remote.json is per developer and git-ignored;
  • the npm start, npm run release and npm run submit scripts in extension/package.json run a pinned Blocks CLI version (@airtable/blocks-cli@3.0.3) through npx, so npm start can be used instead of block run;
  • the dev server rebuilds the panel on every code edit;
  • once it is running, set your adapter URL in the extension: Settings → Advanced. The default URL is set by the DEFAULT_ADAPTER_URL constant in extension/frontend/defaults.ts.

To publish:

npm run release    # block release - pushes the build to the extension in the base
npm run submit     # block submit - Marketplace review

A custom extension lives in one base. To use it in another base, add that base as another remote (block add-remote) and release again.

How the integration works#

Outgoing messages#

  1. The extension builds one message per row and hands the batch to the adapter (POST jobs). Message types and per-messenger limits are described in Message types.
  2. The adapter does not pace sends - the GREEN-API queue does, with the delaySendMessagesMilliseconds instance setting.
  3. Files are sent by upload: when the adapter reaches the row, it downloads the attachment and uploads it to GREEN-API with sendFileByUpload.

Sending without the extension panel - from Airtable automations and external services - is done by the POST send route with an automation key. The request and response format is described in Send from Airtable automations and other services.

Incoming messages#

  1. To write into the base, the adapter needs Airtable OAuth access with data.records:read/write and schema.bases:read. The owner's first identity confirmation already includes it.
  2. When receiving is switched on for an instance, the adapter calls setSettings with webhookUrl = <APP_URL>/api/v1/webhooks/green-api and a random webhookUrlToken. Switching receiving off clears the webhook again.
  3. The adapter writes each webhook into Airtable synchronously and answers GREEN-API with 200. If the write fails (Airtable rate limit, expired token, deleted field, Airtable outage), it answers 503, and GREEN-API re-delivers the same webhook.
  4. A re-delivered message that was already written is recognised by its idMessage and not duplicated.

What the base owner sees when setting up receiving and how statuses are updated is described in Receive incoming messages.

Phone numbers and chat IDs#

How the extension recognises phone numbers, chat IDs and group IDs is described in Phone numbers and chat IDs. When sending, a phone number is normalised to <digits>@c.us: in WhatsApp - any value except a group ID (…@g.us), in Telegram and MAX - only a value formatted like a phone number.

GREEN-API methods#

What each tool does is described in Additional tools. The table below shows which GREEN-API methods it calls.

Where in the extension GREEN-API method
Tools → Check numbers checkWhatsapp / checkAccount
Tools → Chat history checkAccount, getChatHistory. For Telegram and MAX, a phone number is first resolved to the chat ID, because their journals are keyed by chat ID
Tools → Import history lastIncomingMessages, lastOutgoingMessages
Tools → Import contacts getContacts
Instances → Link phone qr
Send tab, message queue showMessagesQueue, clearMessagesQueue

The links lead to the WhatsApp method descriptions.

Adapter API#

All routes are under /api/v1. Every route except oauth/* and webhooks/* needs Authorization: Bearer pk_… (a personal key). Rate limits: 300 requests per minute per IP; oauth/identity - 10 per minute.

Method Route Who Purpose
POST oauth/identity anyone start identity confirmation for a base → authorization URL
POST oauth/claim anyone exchange the completed authorization for the personal key
GET oauth/callback Airtable OAuth redirect target
POST oauth/base owner (re)grant the adapter Airtable access for the base
GET bases/me member base view: Airtable connection, receiving config, instances, the caller
PUT / DELETE bases/inbound owner set / clear the receiving configuration
DELETE bases/airtable owner disconnect the base's Airtable access
GET instances member instances with live state and phone
POST instances owner add an instance (idInstance, apiTokenInstance, apiUrl, name?)
PATCH instances/:id/receive owner switch receiving on / off
DELETE instances/:id owner remove an instance (clears its webhook first)
GET users/me, users member / owner the caller; everyone who confirmed identity
PUT users/:id/instances owner which instances an editor may use
POST users/:id/revoke, DELETE users/:id owner revoke a key / remove a person
POST jobs member send a batch (idInstance, items[], writeBack?)
GET jobs, jobs/:id member recent jobs; one job with per-row results
POST jobs/:id/cancel member stop a running job
GET / POST automation-keys owner list / create automation keys (the key itself is returned once)
DELETE automation-keys/:id owner revoke a key
POST instances/:id/check member check phone numbers on the instance's messenger
POST instances/:id/history, instances/:id/chat-history member import the journal / one chat into the messages table
GET instances/:id/queue, POST instances/:id/queue/clear member GREEN-API queue size; clear it
GET instances/:id/qr member QR code for linking
GET instances/:id/contacts member the messenger's contacts
POST send automation key send one message and wait for the result (format)
POST webhooks/green-api GREEN-API webhook receiver (Bearer webhookUrlToken)

What the adapter stores#

Message content is not kept. The adapter database holds:

  • the Airtable base ID, its receiving configuration and the base's Airtable OAuth tokens;
  • people who confirmed identity: Airtable user ID, email, name, role, a hash of their personal key, and which instances editors may use;
  • instances: idInstance, apiTokenInstance, apiUrl, messenger, state, webhook settings;
  • automation keys: name, instance restriction, a hash of the key, when last used;
  • send jobs: one row per batch and per recipient with status, GREEN-API message ID and error. The message payload is kept only while the row is queued and removed as soon as it is sent or fails.

Incoming webhooks are written straight into Airtable and not stored. The GREEN-API queue is the retry buffer.

Adapter logs

The greenapi-integration library logs request bodies and webhooks at info level. So message text and phone numbers do appear in the adapter's stdout unless log output is filtered.

Development and tests#

npm test                         # adapter unit tests (jest)
cd extension && npm test         # extension unit tests
  • Adapter: NestJS 11, Prisma 7 (prisma/schema.prisma), src/defaults.ts for every constant, src/errors.ts for error codes.
  • Extension: React 16 and Blocks SDK 1.19. Config is stored in globalConfig under the greenapi key (extension/frontend/config), message building is in extension/frontend/send/compose.ts, styling tokens are in extension/frontend/style.css.

License#

Licensed under MIT