When a lawyer dictates a voice note that creates a workspace task, the link between the audio and the resulting record matters for audit purposes. This article covers storing wa_message_id on created records, PHP globals for cross-cutting webhook context, and the workspace UI mic badge.
The Traceability Problem
When a voice note says "remind me about the Sharma hearing next Tuesday", the WhatsApp AI agent creates a workspace reminder. Without traceability, that reminder is an orphan — there is no way to retrieve the original audio, verify the transcript, or audit whether the reminder was created correctly.
Traceability requires: the wa_message_id of the voice note, the wa_file_id (Media API ID), and the transcript — stored on the created record.
PHP Globals for Cross-Cutting Webhook Context
// Set once in the webhook handler, before any handlers are called
class WebhookContext
{
private static array $context = [];
public static function set(array $data): void
{
self::$context = $data;
}
public static function getWaMsgId(): ?string
{
return self::$context['wa_msg_id'] ?? null;
}
public static function getWaFileId(): ?string
{
return self::$context['wa_file_id'] ?? null;
}
public static function getTranscript(): ?string
{
return self::$context['transcript'] ?? null;
}
}
// In the webhook entry point:
WebhookContext::set([
'wa_msg_id' => $incomingMessage['id'],
'wa_file_id' => $incomingMessage['audio']['id'] ?? null,
'transcript' => null, // filled after Whisper runs
]);
// After transcription:
WebhookContext::set([...WebhookContext::getAll(), 'transcript' => $transcribedText]);
Storing Traceability on Created Records
class AddNoteHandler
{
public function handle(string $text, PersonaInterface $persona, PDO $pdo): string
{
$pdo->prepare(
'INSERT INTO workspace_items
(team_id, item_type, title, body, created_by, source_type, wa_message_id, transcript)
VALUES (?, \'note\', ?, ?, ?, \'voice_note\', ?, ?)'
)->execute([
$persona->getTeamId(),
$this->extractTitle($text),
$text,
$persona->getUserId(),
WebhookContext::getWaMsgId(),
WebhookContext::getTranscript(),
]);
return "Note saved. You can review it in the workspace with the original voice recording.";
}
}
What to Watch For
- Media URL expiry — Meta media URLs expire after 5 minutes. If you need to retrieve the original audio later, you must download it at webhook time and store it locally, not just store the media_id URL.
- Transcript privacy — Transcripts contain legal content that may be privileged. Store them with the same access controls as the workspace items they relate to.
- Handler independence — Not every created record comes from a voice note. Handlers must work correctly when
getWaMsgId()returns null (text messages).