Asana Integration
Pilot integrates with Asana to receive tasks and post status updates. When a task is tagged with the pilot tag, Pilot implements it and reports progress back to Asana.
Asana uses tasks and tags as its primary organizational units. Pilot watches for tasks with a specific tag and processes them automatically.
Setup
1. Create an Asana Personal Access Token
- Go to Asana → Settings → Apps → Developer apps
- Click Create new token
- Name it “Pilot Bot” and copy the token
Store your token securely. For production, use environment variables or a secrets manager. Personal access tokens have full access to your Asana account.
2. Find Your Workspace ID
Your workspace ID (GID) can be found:
- Go to any Asana project
- Look at the URL:
https://app.asana.com/0/{workspace_gid}/{project_gid} - The first number after
/0/is your workspace GID
Or use the Asana API:
curl -H "Authorization: Bearer $ASANA_ACCESS_TOKEN" \
https://app.asana.com/api/1.0/workspaces3. Create the pilot Tag
In your Asana workspace:
- Open any task
- Click Add tags
- Type
pilotand create the tag - The tag is now available workspace-wide
4. Configure Pilot
# ~/.pilot/config.yaml
adapters:
asana:
enabled: true
access_token: ${ASANA_ACCESS_TOKEN}
workspace_id: "1234567890123"
pilot_tag: pilot
polling:
enabled: true
interval: 30sConfiguration Reference
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Enable the Asana adapter |
access_token | string | required | Asana personal access token |
workspace_id | string | required | Asana workspace GID |
webhook_secret | string | — | X-Hook-Secret for webhook verification |
pilot_tag | string | "pilot" | Tag that triggers Pilot to process a task |
polling.enabled | bool | false | Enable polling for new tasks |
polling.interval | duration | 30s | Polling interval |
Polling Mode
When polling is enabled, Pilot:
- Finds the
pilottag by name in the workspace - Queries tasks with that tag
- Filters to active (non-completed) tasks
- Processes tasks in order of creation date (oldest first)
Task Processing Flow
1. Find task with 'pilot' tag
2. Skip if has 'pilot-in-progress', 'pilot-done', or 'pilot-failed' tag
3. Add 'pilot-in-progress' tag
4. Execute implementation
5. On success: remove 'pilot-in-progress', add 'pilot-done'
6. On failure: remove 'pilot-in-progress', add 'pilot-failed'Webhook Mode
For real-time task detection, configure Asana webhooks:
1. Set Up Webhook
Webhooks are registered via the Asana API:
curl -X POST https://app.asana.com/api/1.0/webhooks \
-H "Authorization: Bearer $ASANA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"resource": "1234567890123",
"target": "https://your-pilot.example.com/webhooks/asana"
}
}'2. Webhook Handshake
Asana uses a handshake protocol for webhook registration:
- Asana sends a request with
X-Hook-Secretheader - Pilot must respond with the same value in
X-Hook-Secret - This establishes the webhook and provides the secret for future verification
3. Configure Webhook Secret
adapters:
asana:
webhook_secret: ${ASANA_WEBHOOK_SECRET}The webhook secret is provided by Asana during handshake. Store it securely after the initial registration.
Signature Verification
Asana webhooks use HMAC-SHA256 signatures. Pilot validates incoming webhooks against the stored secret.
Supported Events
| Event | Action | Behavior |
|---|---|---|
added | Task created | Process if has pilot tag |
changed | Task updated | Process if pilot tag was just added |
removed | Task deleted | Ignored |
deleted | Task deleted | Ignored |
undeleted | Task restored | Ignored |
Tags
Pilot uses tags to track task status:
| Tag | Purpose |
|---|---|
pilot | Triggers Pilot to process the task |
pilot-in-progress | Applied while Pilot is working |
pilot-done | Applied after successful completion |
pilot-failed | Applied if execution fails |
Tag Operations
Asana tags are managed via their GID. On startup, Pilot:
- Looks up the
pilottag by name - Caches the GID for efficient polling
- Creates status tags if they don’t exist
Status Updates
Pilot posts comments (stories) to Asana tasks as it works:
| Phase | Comment |
|---|---|
| Started | ”Pilot started working on this task” |
| Completed | ”Successfully completed — PR #42” with link |
| Failed | ”Failed: error details” |
Comment Formats
Pilot supports both plain text and HTML comments:
// Plain text
client.AddComment(ctx, taskGID, "Pilot completed the task")
// HTML (for rich formatting)
client.AddHTMLComment(ctx, taskGID, "<p>✅ <strong>Completed</strong> — <a href='...'>PR #42</a></p>")Priority Mapping
Asana doesn’t have a built-in priority field, but Pilot recognizes priority tags:
| Tag Name | Pilot Priority |
|---|---|
urgent, critical, URGENT | Urgent |
high, High, HIGH | High |
medium, Medium | Medium |
low, Low, LOW | Low |
Task Conversion
Asana tasks are converted to Pilot tasks:
| Asana Field | Pilot Field | Format |
|---|---|---|
gid | Task ID | ASANA-{gid} |
name | Title | Direct |
notes | Description | Plain text |
html_notes | Description (alt) | HTML stripped |
tags | Labels | Tag names |
projects[0].name | ProjectName | First project |
permalink_url | TaskURL | Direct |
created_at | CreatedAt | ISO 8601 |
PR Attachments
When Pilot creates a PR, it can add an external attachment to the Asana task:
client.AddAttachment(ctx, taskGID, prURL, "Pull Request #42")This creates a clickable link in the task’s attachments section.
API Operations
Pilot uses these Asana API endpoints:
| Operation | Endpoint | Purpose |
|---|---|---|
| Get Task | GET /tasks/{gid} | Fetch task details |
| Update Task | PUT /tasks/{gid} | Update completion status |
| Add Story | POST /tasks/{gid}/stories | Post comments |
| Add Tag | POST /tasks/{gid}/addTag | Add status tags |
| Remove Tag | POST /tasks/{gid}/removeTag | Remove status tags |
| Get Tags | GET /workspaces/{gid}/tags | List workspace tags |
| Find Tag | GET /workspaces/{gid}/tags | Find tag by name |
| Get Tasks by Tag | GET /tags/{gid}/tasks | Query tagged tasks |
| Add Attachment | POST /tasks/{gid}/attachments | Link PR |
Multi-Project Support
Asana tasks can belong to multiple projects. Pilot uses the first project for context:
# Task belongs to "Backend" and "Sprint 42" projects
# Pilot sees: ProjectName = "Backend"Duplicate Prevention
Available since v2.10.0. All non-GitHub pollers now share this reliability feature.
The Asana poller persists processed task state to SQLite via a ProcessedStore. This means:
- Survives restarts and hot upgrades — tasks that were already dispatched are not re-dispatched after a restart.
- Automatic rollback on failure — if execution fails, the task is unmarked so Pilot can retry it on the next poll cycle.
- Loaded on startup — previously processed tasks are restored from the database before the first poll.
No additional configuration is required. The store is enabled automatically when Pilot uses SQLite for memory.
Automatic Task Completion
When Pilot creates a PR for an Asana task, the task is automatically marked as completed in Asana via the CompleteTask API call.
- Completion happens as part of the success notification flow (after the PR comment is posted).
- Non-blocking — if the
CompleteTaskcall fails (e.g., network error, permission issue), the PR is still created and the task is still marked aspilot-donevia tags. A warning is logged instead.
Retry Behavior
If execution fails:
pilot-in-progresstag is removedpilot-failedtag is added- Task is NOT marked as processed (allowing retry)
- Removing
pilot-failedtag allows Pilot to retry
Troubleshooting
Task Not Being Picked Up
- Verify the task has the
pilottag - Check that task is not completed
- Ensure no
pilot-in-progressorpilot-donetags exist - Verify workspace ID is correct
- Check token has access to the workspace
Tag Not Found
- Ensure the tag exists in the workspace
- Tag names are case-sensitive
- Create the tag manually if it doesn’t exist
- Check Pilot logs for “pilot tag not found” errors
Webhook Not Triggering
- Verify webhook URL is publicly accessible
- Check webhook registration succeeded
- Ensure webhook handshake completed
- Verify webhook secret matches
- Check Asana webhook delivery status
Authentication Errors
- Verify token hasn’t expired
- Check token has workspace access
- Regenerate token if compromised
- Ensure token isn’t rate-limited
Rate Limiting
Asana has API rate limits. If you hit them:
- Increase polling interval
- Use webhooks instead of polling
- Reduce concurrent operations
- Check for runaway retry loops