Send WhatsApp notifications from Asana task webhooks
Asana's own notifications get buried fast — between every comment, every subtask, every "liked this task" email, the one update you actually care about (a client-facing deliverable marked done, a task reassigned to someone on PTO) disappears. WhatsApp doesn't have that problem: people open it within minutes. This guide builds a direct webhook bridge from Asana to WhatsApp, without routing through a third-party automation tool.
Why go direct instead of Zapier or Make
Asana webhooks push a signed JSON payload to a URL you control for every change on a project: task created, task completed, assignee changed, due date moved. If you already run a backend, translating that payload into a WhatsApp message is a dozen lines of code — no separate subscription, no extra service that can go down independently of your own stack. If you'd rather not maintain that endpoint yourself, n8n, Make or Zapier all have Asana triggers and remain the faster path for teams that don't want to touch code at all.
Step 1: register the webhook
Asana webhooks are created via a POST to their API, targeting the resource you want to watch (usually a project GID) and the callback URL that will receive events.
curl -X POST "https://app.asana.com/api/1.0/webhooks" \
-H "Authorization: Bearer YOUR_ASANA_TOKEN" \
-d "resource=PROJECT_GID" \
-d "target=https://yourdomain.tld/asana-webhook"
Asana performs a handshake before the webhook is active: it sends an empty POST with an X-Hook-Secret header to your callback URL, and expects you to echo that same header back in your response within a few seconds. Your endpoint needs to be live and reachable before you register the webhook, not after.
// routes/web.php
Route::post('/asana-webhook', function (Request $request) {
if ($secret = $request->header('X-Hook-Secret')) {
return response('', 200)->header('X-Hook-Secret', $secret);
}
// handle real events below
return response()->json(['ok' => true]);
});
Step 2: filter for the events you care about
Asana batches multiple changes into a single webhook delivery, so you'll get a stream of events in one payload. Most of them are noise (a task moved between sections, a follower added) unless you filter for the specific action and resource_type combination you want — typically action: "changed" on a task's completed field, or parent changes for reassignment.
Route::post('/asana-webhook', function (Request $request) {
if ($secret = $request->header('X-Hook-Secret')) {
return response('', 200)->header('X-Hook-Secret', $secret);
}
foreach ($request->input('events', []) as $event) {
if (($event['action'] ?? null) !== 'changed'
|| ($event['resource']['resource_type'] ?? null) !== 'task') {
continue;
}
$change = $event['change']['field'] ?? null;
if ($change !== 'completed') {
continue;
}
Http::withToken(config('services.textmeflow.api_key'))
->post('https://textmeflow.eu/v1/messages', [
'to' => config('services.asana_notify.phone'),
'text' => "Task {$event['resource']['gid']} was marked complete in Asana.",
]);
}
return response()->json(['ok' => true]);
});
The task payload from a changed event only gives you a GID by default — fetch GET /api/1.0/tasks/{gid} with your Asana token if you want the actual task name in the WhatsApp message rather than an opaque ID.
Step 3: notify a team, not a single number
A single phone number in config is fine for testing, but in production you'll usually want the whole team, or a specific person depending on who the task is assigned to. Send to a WhatsApp group when the update is relevant to everyone, or look up the assignee's number from your own user table and send 1:1. Either way, watch the rate limits — when one event needs to reach several recipients, send sequentially with a short delay rather than firing requests in parallel, and check the 202/429 response from /v1/messages to know whether a send actually queued.
Secure the endpoint
Asana doesn't sign delivery payloads with anything comparable to TextMeFlow's own HMAC verification for inbound WhatsApp replies — the X-Hook-Secret only covers the handshake, not every subsequent event. Store the secret Asana gives you during the handshake and require it as a header (or a secret query parameter) on every later call to your endpoint, so it can't be spoofed by a third party who guesses the URL. If you're also processing inbound WhatsApp messages on the same backend, make sure those go through TextMeFlow's proper webhook signature verification — the two integrations are independent and shouldn't share a trust assumption.
Extending it
The same pattern covers other Asana triggers: a new task created in a specific project, a due date within 24 hours, a custom field changing status. Pair it with TextMeFlow's own webhook events if you want the loop to close in both directions — for example, a client replying "approved" on WhatsApp automatically marking the linked Asana task complete via the Asana API.
Get started
Want to try this yourself? TextMeFlow's free plan includes 50 messages per month with no expiry date — enough to build and test this entire Asana integration before you pay anything. Start for free.
Zelf WhatsApp-berichten versturen via API?
Gratis voor altijd tot 50 berichten/maand. QR scannen en binnen 5 minuten verstuur je je eerste bericht.
Gratis voor altijd