Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Webhooks

Webhooks let you register an HTTP endpoint to receive real-time event notifications. When AiMessage detects a new message or reaction, it POSTs a JSON payload to every registered URL that subscribes to that event type.


Register a webhook

POST /api/v1/webhooks

Request body:

{
  "url": "http://127.0.0.1:8080/webhook",
  "events": ["message.received", "reaction.added"]
}
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" -d '{"url": "http://127.0.0.1:8080/webhook", "events": ["message.received", "reaction.added"]}' http://localhost:3001/api/v1/webhooks

With a secret

The secret field is optional. When provided, AiMessage signs every delivery using HMAC-SHA256 and includes the signature in the X-Webhook-Signature header in the format sha256=<hex>.

curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" -d '{"url": "http://127.0.0.1:8080/webhook", "events": ["message.received"], "secret": "my-secret-token"}' http://localhost:3001/api/v1/webhooks

Verifying the signature:

Compute HMAC-SHA256(key=secret, message=raw_request_body) and compare the hex digest to the value after sha256= in the X-Webhook-Signature header. Use a constant-time comparison to prevent timing attacks.

Python example:

import hmac, hashlib

def verify(secret: str, body: bytes, header: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header)

For local integrations, binding your webhook listener to 127.0.0.1 (as shown above) prevents external access and is recommended for single-machine setups.

Available events

EventWhen it fires
message.receivedAn incoming message is detected in chat.db
message.sentAn outgoing message sent by AiMessage is confirmed in chat.db
reaction.addedA reaction is added to a message
reaction.removedA reaction is removed from a message

Payload format

All events use the same envelope:

{
  "type": "message.received",
  "data": {
    "id": "94711",
    "guid": "F568F54A-1234-5678-ABCD-000000000000",
    "conversation_id": "iMessage;-;+15551234567",
    "sender": "+15551234567",
    "body": "Hey!",
    "attachments": [],
    "timestamp": "2026-03-23T23:49:54Z",
    "is_from_me": false,
    "status": "delivered"
  }
}

The data object shape varies by event type. For reaction.added and reaction.removed, it includes the reaction type and the ID of the message being reacted to.


Retry behavior

Failed deliveries (non-2xx response or connection error) are retried up to 3 times with backoff: 1 second, then 5 seconds. After 3 failures the delivery is dropped.


List webhooks

GET /api/v1/webhooks
curl -H "X-API-Key: $KEY" http://localhost:3001/api/v1/webhooks

Response:

[
  {
    "id": "a1b2c3d4-...",
    "url": "http://127.0.0.1:8080/webhook",
    "events": ["message.received"],
    "created_at": "2026-03-23T12:00:00Z"
  }
]

Delete a webhook

DELETE /api/v1/webhooks/{id}
curl -X DELETE -H "X-API-Key: $KEY" http://localhost:3001/api/v1/webhooks/a1b2c3d4-...

Returns 204 No Content on success.