Skip to main content

Developer docs

Alfred API.

POST a command from any HTTP client. Slack, Zapier, n8n, your backend.

01

Get an API key

Open your portal → Settings → Integrations → API keys. Click New key, name it (e.g. slack-bot), and copy the token. The full secret is shown once — store it in your secret manager immediately.

Tokens look like ak_a1b2c3... and are scoped to a single business. Revoke at any time from the same screen.

02

The POST contract

POST /api/embedded/alfred/command — request
curl -X POST https://alfred-intelligence.com/api/embedded/alfred/command \
  -H "Authorization: Bearer ak_YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "command": "Book 20 botox consultations this month",
    "channel": "embedded"
  }'

Required: command (string, ≤ 2000 chars).
Optional: channel — embedded (default) or in_app; any other value is recorded as embedded. Sets where the command came from for the audit log; doesn't affect routing.

Response (200)
{
  "command_id": "0a4f...",
  "job_id": "f792...",
  "status": "queued",
  "confirmation": "Started \"Book consultations\" for this month. Targeting 20 contacts. You'll get 3 updates during the run.",
  "estimated_result": "~20 consults booked"
}

Response status codes

  • queued — Skill resolved. Job is running. Contacts being enrolled now.
  • needs_clarification — Ambiguous command. Response includes a clarifying_question + candidates. Send a follow-up command picking one.
  • no_match — No Skill matched. Response confirmation explains why. Try a more specific command.
  • error — Internal error. Job was logged but not started. Retry later.
03

Examples

JavaScript / Node

const res = await fetch('https://alfred-intelligence.com/api/embedded/alfred/command', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.ALFRED_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    command: 'Win back lapsed patients',
    channel: 'embedded',
  }),
})
const data = await res.json()
console.log(data.confirmation)

Slack outgoing webhook

# In Slack: Apps → Outgoing Webhooks
# Trigger word: alfred
# URL: https://your-relay-service/slack-to-alfred

# In your relay:
const text = req.body.text.replace(/^alfred\s*/i, '')
const res = await fetch('https://alfred-intelligence.com/api/embedded/alfred/command', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.ALFRED_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ command: text, channel: 'embedded' }),
})
const data = await res.json()
return { text: data.confirmation }

n8n / Zapier

# n8n HTTP Request node configuration:
# Method:  POST
# URL:     https://alfred-intelligence.com/api/embedded/alfred/command
# Headers:
#   Authorization: Bearer {{ $env.ALFRED_API_KEY }}
#   Content-Type:  application/json
# Body (JSON):
{
  "command": "{{ $json.user_message }}",
  "channel": "embedded"
}
04

Auth errors

  • 401 missing_authorization — no Bearer header.
  • 401 invalid_or_revoked_key — token doesn't match a row or was revoked.
  • 403 insufficient_scope — key doesn't include alfred.command. Mint a new one.
  • 429 rate_limited — too many calls per business. Back off ~1 minute.
  • 410 business_gone — the business that owns this key was deleted.
05

Rate limits + CORS

Per-business AI bucket. Same budget shared between in-app and embedded API calls. Headers on every response indicate remaining capacity for the current window.

CORS is wide open (Access-Control-Allow-Origin: *). Preflight (OPTIONS) is handled. You can call this endpoint directly from browser JavaScript if you're comfortable shipping the key — but for any user-facing app, proxy through your backend.

07

Not in v1 (yet)

  • Webhooks for job state changes (planned)
  • List recent commands via the API (use the in-app contact timeline for now)
  • Stream Alfred's thinking (planned)
  • Multi-step conversations within a single API call (planned)

Ready to ship?

Mint your first key in the portal.

Open the portal