Documentation
Send messages to your phone with a single curl command. No SDK required.
Quickstart
Get your first notification in under 60 seconds:
1. Sign up
Create an account at airlog.to. You'll get a channel with a unique slug.
2. Send a message
# Plain text
curl -d "Deploy completed successfully" https://airlog.to/your-slug
# JSON with level
curl -H "Content-Type: application/json" \
-d '{"text":"CPU at 95%","level":"error","title":"Server Alert"}' \
https://airlog.to/your-slug
curl -d "hello from docs" https://airlog.to/your-slug
3. Get notified
Messages appear instantly in the Airlog dashboard. Warnings and errors trigger push notifications.
Concepts
Channels — Each channel has a unique slug URL. Think of them as separate inboxes for different apps or services.
Messages — Text or JSON payloads sent to a channel. Each has a level (info, warn, error, critical).
Levels — Control notification behavior. Info is silent, warn triggers push, error adds badge, critical adds sound.
Authentication
Ingest (sending messages)
No authentication needed. Just POST to your channel's slug URL. The slug itself acts as an API key.
REST API (managing channels)
Requires a Bearer token from OAuth login.
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
https://airlog.to/api/channels
Sign in flow:
- Redirect user to
/auth/githubor/auth/google - After OAuth, user is redirected to
/auth/callback?code=AUTH_CODE - Exchange the code for a JWT:
curl -X POST -H "Content-Type: application/json" \
-d '{"code":"AUTH_CODE"}' \
https://airlog.to/auth/token
# Response
{ "token": "eyJhbGciOi..." }
Get the authenticated user's profile.
curl -H "Authorization: Bearer TOKEN" https://airlog.to/api/me
# Response
{
"id": "6e46bcc8-...",
"email": "user@example.com",
"name": "User Name",
"plan": "free"
}
Send Message
Send a message to a channel. Auto-detects content type.
Plain text
curl -d "Server restarted" https://airlog.to/my-app
JSON
curl -H "Content-Type: application/json" \
-d '{"text":"Disk usage 92%","level":"warn","title":"Storage"}' \
https://airlog.to/my-app
Response
{
"id": "9f05f0c6-...",
"channel": "a1b2c3d4e5f6",
"timestamp": "2026-03-15T12:00:00Z"
}
JSON Format
| Field | Type | Description |
|---|---|---|
text required | string | Message body |
level optional | string | info, warn, error, critical (default: info) |
title optional | string | Short title for the message |
tags optional | string[] | Tags for filtering |
data optional | object | Arbitrary JSON metadata |
Message Levels
| Level | Push | Badge | Sound | Use case |
|---|---|---|---|---|
info | No | No | No | Deploy logs, status updates |
warn | Yes | No | No | High memory, slow queries |
error | Yes | Yes | No | Failed requests, exceptions |
critical | Yes | Yes | Yes | Server down, data loss |
Channels API
List all your channels. Requires auth.
# Response
[{
"id": "5c50146a-...",
"slug": "a1b2c3d4e5f6",
"name": "my-app",
"created_at": "2026-03-15T10:00:00Z"
}]
Create a new channel.
curl -X POST -H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"my-app"}' \
https://airlog.to/api/channels
Generate a new slug. Old slug stops working immediately.
Delete a channel and all its messages.
Members API
Invite team members to your channels with role-based access.
List all members of a channel.
Add a member to a channel.
curl -X POST -H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"user_id":"USER_ID","role":"viewer"}' \
https://airlog.to/api/channels/{id}/members
| Role | Description |
|---|---|
owner | Full control (creator) |
editor | Can manage channel settings |
viewer | Read-only access to messages |
Remove a member from a channel.
Update a member's role.
Messages API
List messages. Cursor-based pagination, newest first.
| Param | Type | Description |
|---|---|---|
limit optional | int | Max messages (default: 50, max: 100) |
cursor optional | string | Message ID for pagination |
Get a single message by ID.
WebSocket
Connect to receive real-time messages:
const ws = new WebSocket(
`wss://airlog.to/ws/channels/${channelId}?token=${token}`
);
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log(msg.level, msg.text);
};
MCP (AI Tools)
Airlog ships an MCP server for Claude Code, Cursor, and other AI tools.
Install
# Install
go install github.com/airlog/airlog/cmd/mcp@latest
# Claude Code
claude mcp add airlog -e AIRLOG_USER_ID=YOUR_USER_ID -- airlog-mcp
# Cursor (.cursor/mcp.json)
{
"mcpServers": {
"airlog": {
"command": "airlog-mcp",
"env": { "AIRLOG_USER_ID": "YOUR_USER_ID" }
}
}
}
Available Tools
| Tool | Description |
|---|---|
airlog_send | Send a message to a channel |
airlog_channels | List your channels |
airlog_history | View recent messages |
airlog_create_channel | Create a new channel |
Examples
Shell script
#!/bin/bash
if pg_dump mydb > backup.sql; then
curl -d "Backup completed ($(du -h backup.sql | cut -f1))" \
https://airlog.to/ops
else
curl -H "Content-Type: application/json" \
-d '{"text":"Backup FAILED","level":"critical"}' \
https://airlog.to/ops
fi
Python
import requests
requests.post("https://airlog.to/my-app", json={
"text": "Payment failed",
"level": "error",
"title": "Stripe",
"data": {"customer_id": "cus_123"}
})
Node.js
fetch("https://airlog.to/my-app", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: "Memory at 95%",
level: "warn",
tags: ["server-1"]
})
});
GitHub Actions
# .github/workflows/deploy.yml
- name: Notify Airlog
if: always()
run: |
if [ "${{ job.status }}" = "success" ]; then
curl -d "Deploy ${{ github.sha }} succeeded" https://airlog.to/ops
else
curl -H "Content-Type: application/json" \
-d '{"text":"Deploy failed","level":"critical","tags":["ci"]}' \
https://airlog.to/ops
fi
Rate Limits
| Plan | Messages/day | Channels | Retention |
|---|---|---|---|
| Free | 100 | 3 | 7 days |
| Pro ($9/mo) | 10,000 | Unlimited | 90 days |
| Team ($29/mo) | 50,000 | Unlimited | 1 year |
Rate limited responses return 429 Too Many Requests.
Errors
| Code | Meaning |
|---|---|
400 | Bad request — empty body or invalid JSON |
401 | Unauthorized — missing or invalid Bearer token |
404 | Channel not found — invalid slug |
413 | Payload too large — body exceeds 64KB |
429 | Rate limited — daily quota exceeded |
500 | Internal server error |
All errors return JSON: {"error": "description"}