# Baserow Figma Integration - Sync Design Tokens and Activity

> Webhook events from Figma - comments, library publishes - land in a Baserow table, while a script pushes design tokens to Figma Variables via REST API.

Source: https://opennix.org/en/docs/baserow/integrations/figma-integration/


There is no built-in button that syncs Baserow and Figma directly, but the open-source edition can still bridge the two: a script reads rows from a Baserow table and pushes them into Figma Variables through the REST API, while in the other direction Figma webhooks - comments, library publishes, status changes - post events into a Baserow automation that creates a row for each one; neither direction needs a plugin, and both keep working even when nobody has the file open.

## How It Works

Design and data usually live in separate tools: designers work in Figma, while product data, copy, and task status live in Baserow. The traditional bridge is a plugin, but a plugin depends on a designer remembering to open it and click "Sync." Calling the Figma REST API directly works differently - it does not care whether the file is open, and it can run on a schedule or in response to an event.

There are two independent architecture patterns:

1. **Variables sync (Baserow → Figma):** design token values (colors, copy, numbers) live in Baserow and get pushed to Figma Variables via the REST API on a schedule.
2. **Activity log (Figma → Baserow):** Figma webhooks log comments and file updates straight into a Baserow table for a single project overview.

This is more resilient than a plugin: the script talks to Figma's server directly and keeps working even if no one has the file open.

## Pattern A: Variables Sync (Baserow → Figma)

Instead of a designer typing hex codes or copy by hand, design layers get bound to Figma Variables, and Baserow acts as the database backing their values. When marketing updates a price or a tagline in Baserow, a script pushes that change straight into Figma.

### Use Cases

- **Localization:** text strings for different languages live in Baserow columns.
- **Theming:** color tokens for light mode and dark mode.
- **Product data:** live pricing or inventory numbers that show up in design mockups.

### Step 1: Prerequisites

- **Figma personal access token.** From the Figma file browser, open the account menu in the top-left corner, go to **Settings → Security**, and click **Generate new token** under **Personal access tokens**.
- **Baserow database (API) token.** See [Baserow database (API) tokens](/docs/baserow/webhook-api/personal-api-tokens/) for how to create one and what permissions it needs. Never hand these tokens to anyone you do not want accessing your data.
- **Figma file key.** The alphanumeric value in the design file's URL, sitting between the file type and the file name - for example `figma.com/{file_type}/{file_key}/{file_name}`.

The Figma token is passed in the `X-Figma-Token` header on every API request.

### Step 2: The Bridge Script

Figma's API is passive - it never sends data on its own, so an external runner (a GitHub Action or a plain cron script) has to do two things:

1. `GET` data from Baserow - for example, a "Brand Assets" table - via the [Baserow REST API](/docs/baserow/webhook-api/database-api/).
2. `POST` the updates to Figma's `/v1/files/:file_key/variables` endpoint, which bulk-creates, updates, and deletes variables and variable collections.

```python
import requests

# Configuration
BASEROW_API_URL = "https://api.baserow.io/api/database/rows/table/YOUR_TABLE_ID/"
FIGMA_API_URL = "https://api.figma.com/v1/files/YOUR_FILE_KEY/variables"
HEADERS_BASEROW = {"Authorization": "Token YOUR_BASEROW_TOKEN"}
HEADERS_FIGMA = {"X-Figma-Token": "YOUR_FIGMA_TOKEN", "Content-Type": "application/json"}

def sync_design_tokens():
    # 1. Fetch "Single Source of Truth" from Baserow
    response = requests.get(BASEROW_API_URL, headers=HEADERS_BASEROW, params={"user_field_names": "true"})
    data = response.json()

    # Initialize the list to hold all our value updates
    value_updates = []

    # 2. Iterate through rows and prepare Figma updates
    for row in data['results']:
        variable_id = row['Figma Variable ID'] # e.g., "VariableID:123"
        mode_id = row['Figma Mode ID']         # e.g., "1:0" (Required by Figma)
        new_value = row['Value']               # e.g., "Welcome Home"

        # 3. Append to our update list using Figma's required schema
        value_updates.append({
            "variableId": variable_id,
            "modeId": mode_id,
            "value": new_value
        })

    # 4. Construct the final payload and push to Figma
    payload = {
        "variableModeValues": value_updates
    }

    # 5. Make the atomic POST request
    response = requests.post(FIGMA_API_URL, headers=HEADERS_FIGMA, json=payload)

    if response.status_code == 200:
        print(f"Successfully updated {len(value_updates)} variable values!")
    else:
        print(f"Failed to update variables. Error: {response.status_code}")
        print(response.json())

sync_design_tokens()
```

Figma expects specific data types - strings, numbers, or RGBA objects. The script acts as the translator between how a value is stored in Baserow and the format Figma expects.

## Pattern B: Project Tracking (Figma → Baserow)

When the goal is to track the design process itself rather than its content, use Figma webhooks: they let you observe events in a file - a collaborator leaving a comment, or a new version appearing in the file's history - and turn Baserow into a live activity feed for the design team.

Creating a webhook is a Tier 2 operation, so the Figma personal access token must be issued with the `webhooks:write` scope.

### Use Cases

- **Design QA:** a new comment on a file automatically creates a task in Baserow.
- **Version control:** a library publish logs the version history in Baserow.

### Figma Event Types

| Event Type | Best Use Case | Key Data Sent to Baserow |
|---|---|---|
| `FILE_COMMENT` | Design QA: capture designer feedback and turn it into actionable tasks | comment text, @mentions, and timestamp |
| `DEV_MODE_STATUS_UPDATE` | Handoff tracking: know exactly when a frame is ready for dev | node ID, status (`READY_FOR_DEV`), and change message |
| `LIBRARY_PUBLISH` | Version control: log when a design system component is updated | list of created/modified components and variables |

### Step 1: Set Up the Baserow Automation

1. Create a **"Design Log"** table in Baserow.
2. Create an automation with the **"Receive an HTTP request (webhook)"** trigger.
3. Copy the webhook URL.

For more on how the trigger itself receives requests, see [Webhooks in Baserow](/docs/baserow/webhook-api/webhooks/).

### Step 2: Create the Figma Webhook

The listener is registered with a `POST` request - from a terminal or a tool like Postman. By default, Figma immediately sends a `PING` event to the endpoint, confirming the webhook is live and will receive updates.

```bash
curl -X POST https://api.figma.com/v2/webhooks \
-H "X-Figma-Token: YOUR_PERSONAL_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
  "event_type": "FILE_COMMENT",
  "context": "file",
  "context_id": "YOUR_FILE_KEY",
  "endpoint": "YOUR_BASEROW_WEBHOOK_URL",
  "passcode": "my_secure_secret",
  "status": "ACTIVE",
  "description": "Sync Figma comments to Baserow Task Log"
}'
```

To track every file in a project or team, change `context` to `project` or `team` and pass the matching ID instead of a file key.

Always set a `passcode`: Figma includes this string in every request it sends, and the Baserow side can check it to confirm a payload genuinely came from Figma rather than an unrelated source.

### Step 3: Map the Figma Payload to a Baserow Row

When a designer leaves a comment, Figma sends a fairly large JSON payload. The Create Row action needs to map specific fields:

- `triggered_by.handle` → the "Assignee" field.
- `comment[0].text` → the "Task Description" field.
- `https://www.figma.com/file/YOUR_FILE_KEY?node-id=...` → the "URL" field.

### Troubleshooting Common Issues

- **401 Unauthorized:** the database token needs Write permission when Figma is the side pushing data in, and the header value must be prefixed with the word `Token`.
- **The 30-minute rule:** the `FILE_UPDATE` event only fires after 30 minutes of inactivity in the file - use `FILE_COMMENT` for instant logging instead.
- **Delivery verification:** the `GET /v2/webhooks/:webhook_id/requests` endpoint shows a log of every attempt Figma made to reach your webhook.
- **Color formatting:** Figma variables expect color as a decimal RGBA object (for example, `{"r": 1, "g": 0.5, "b": 0, "a": 1}`) rather than a hex code - make sure Baserow values match that shape.

## Frequently Asked Questions

**Do both patterns need to be set up together?** No - variables sync and activity logging are independent. Enable only the design-token push, only the Figma event log, or both, depending on the team's needs.

**Does this integration require a Figma plugin?** No - both patterns work directly through the Figma REST API and webhooks, with no plugin to install and no manual step to open a file and trigger a sync.

**How do I get an instant reaction to a file change instead of waiting up to 30 minutes?** Use the `FILE_COMMENT` event instead of `FILE_UPDATE` - it fires right after the designer's action rather than after a period of inactivity in the file.

Related reading: the [Baserow REST API overview](/docs/baserow/webhook-api/database-api/) for direct table calls without webhooks, and the [Tally integration](/docs/baserow/integrations/tally-integration/) as another example of accepting external data through a webhook automation trigger.

