This project contains the scripts and services that sync Asana data into Airtable so the Asana Data Layer base can stay durable and queryable outside Asana.
The API fetches the goal directly from Asana after each webhook event, then flattens the goal into Airtable-friendly fields. The default mapping includes:
- Goal name and GID
- Permalink URL
- archived flag
- owner and team metadata
- due date
- notes and HTML notes
- time period metadata
- current status title, text, color, author, and timestamp
- metric values and unit metadata
- parent goal IDs and names
- raw goal JSON for anything else we may need later
app.py: FastAPI service with webhook and admin sync endpointsregister_goal_webhooks.py: creates one webhook per goal in the CSV exportgoals_export_2026-04-13.csv: source list of goals to watch
- Create a virtualenv and install dependencies:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt-
Copy
.env.exampleinto your runtime environment. -
Make sure the Airtable table already contains a merge field named
Asana Goal GID, or updateAIRTABLE_MERGE_FIELD. -
Run the API:
uvicorn app:app --host 0.0.0.0 --port 8000- Expose the service publicly, for example with ngrok:
ngrok http 8000- Set
PUBLIC_BASE_URLto the public HTTPS URL and register the goal webhooks:
python3 register_goal_webhooks.pyGET /healthPOST /webhooks/asana/{goal_gid}POST /admin/sync-goal/{goal_gid}POST /admin/sync-csvGET /admin/config
If ADMIN_API_KEY is set, pass it as X-Admin-Key for the admin endpoints.
The service can filter outgoing fields to only the columns that already exist in Airtable if AIRTABLE_FILTER_TO_EXISTING_FIELDS=true. That lets us include richer goal metadata without breaking if your table is still catching up.
live_sync_service.py is the long-running FastAPI service for the Asana Data Layer base (app3mkbiuKcaANHa7).
It does three things:
- syncs goal changes from a workspace webhook into
Asana Goals - syncs project changes from the same workspace webhook into
Asana Projects - syncs task changes from per-project webhooks into
Asana Tasks
If Asana Tasks does not exist yet and AUTO_CREATE_TASKS_TABLE=true, the service will create it through the Airtable metadata API the first time it needs it.
- Set these environment variables:
export ASANA_ACCESS_TOKEN='...'
export AIRTABLE_TOKEN='...'
export AIRTABLE_BASE_ID='app3mkbiuKcaANHa7'
export ASANA_WORKSPACE_GID='1204848198937008'
export PUBLIC_BASE_URL='https://your-public-url.example.com'
export ADMIN_API_KEY='choose-a-random-secret'- Run the API:
uvicorn live_sync_service:app --host 0.0.0.0 --port 8000- Expose it publicly, for example:
ngrok http 8000- Bootstrap webhooks and, if needed, the tasks table:
curl -X POST \
-H "X-Admin-Key: $ADMIN_API_KEY" \
"$PUBLIC_BASE_URL/admin/bootstrap"- Backfill the current Asana data into Airtable:
curl -X POST -H "X-Admin-Key: $ADMIN_API_KEY" "$PUBLIC_BASE_URL/admin/backfill/goals"
curl -X POST -H "X-Admin-Key: $ADMIN_API_KEY" "$PUBLIC_BASE_URL/admin/backfill/projects"
curl -X POST -H "X-Admin-Key: $ADMIN_API_KEY" "$PUBLIC_BASE_URL/admin/backfill/tasks"GET /healthPOST /webhooks/asana/workspacePOST /webhooks/asana/project/{project_gid}POST /admin/bootstrapPOST /admin/backfill/goalsPOST /admin/backfill/projectsPOST /admin/backfill/tasksPOST /admin/backfill/allGET /admin/config