Docs / API / Reference

AMDY API reference

AMDY.IO exposes a real-time AMD Detection WebSocket for streaming call audio, plus a small set of API-key authenticated REST routes for registration, configuration and per-IP settings. This page documents only what is verified in the AMDY source tree as of 2026-08-28. Anything not verified is flagged "contact us" so you do not integrate against a guess.

New to AMDY? Start with the AMD Detection API client guide for the WebSocket protocol and classification codes. The platform install guides cover Asterisk, FreeSWITCH and Issabel.

What endpoints does AMDY expose?

Two surfaces, both public-internet-facing:

  • A WebSocket endpoint for live AMD detection: your telephony server streams 8 kHz PCM and receives a classification.
  • A set of REST routes under /api/v1/* for account and IP management, used by install scripts and polled by the detection servers.

What is the AMD Detection WebSocket endpoint?

FieldValue
URLws://api.amdy.io:2700
TransportPlain WebSocket (no TLS variant verified in source)
AuthX-API-Key header on connect, read from /etc/amdy/api-key
Audio format8000 Hz, 16-bit, mono, raw PCM (signed little-endian)

The first text frame must be a config frame: {"config":{"sample_rate":8000,"VID":"<caller id name or Unknown>"}}. After that, binary PCM frames follow, one per chunk. A final text frame {"eof":1} closes the stream and forces a result.

FreeSWITCH exception: mod_audio_fork cannot send custom headers on the WebSocket upgrade, so fork connections carry no X-API-Key and are gated by IP allowlist instead.

A TLS (wss://) variant is not present in any AMDY source in this repository. Contact us to confirm availability for your deployment.

What REST routes exist under /api/v1?

Four API-key authenticated routes. All four use the same auth check and are scoped to the client that owns the key.

MethodPathPurpose
POST/api/v1/ips/registerRegister a server IP and activate the account
GET/api/v1/configClient-level detection config, polled by detection servers
GET/api/v1/client-settingsPer-IP settings bundle (rate-limited)
GET/api/v1/ipsList IPs owned by the client, with their settings

There is also a public, unauthenticated health endpoint: GET /api/health. It returns platform-wide stats (status, detections_7d, human_pct_7d, p95_latency_ms, updated_at), cached for 5 minutes, with no per-client data.

How do I authenticate?

Every /api/v1/* route accepts either:

  • Authorization: Bearer <key>
  • X-API-Key: <key>

The key is looked up in api_keys. Unknown key returns 401; a key whose status is not active returns 403 on register and 401 on the read routes.

Keys are stored on installed servers at /etc/amdy/api-key (mode 600) and referenced from /etc/amdy/amdy.conf.

What is the API key format?

AMDY keys are not 64-character hex strings. The real format, verified in the minting code, is:

  • On signup: amd_live_ + crypto.randomBytes(12) as hex (24 hex chars, 32 chars total including the prefix).
  • On manual creation via the portal: amd_live_ + crypto.randomBytes(16) as hex (32 hex chars, 40 chars total).

The legacy sk-$ENV-* prefix has been retired. If your integration still expects a 64-character hex string, please contact us before upgrading.

How do I register a server IP?

POST /api/v1/ips/register activates the account and mirrors the IP into TimescaleDB for analytics attribution.

Body fieldRequiredNotes
ipnoValidated with net.isIP; auto-detected from X-Forwarded-For / X-Real-IP when absent
descriptionnoDefaults to Installed via installamd-v2.sh, truncated to 191 chars

Success (200):

{
  "ok": true,
  "activated": true,
  "account_activated_now": true,
  "client_id": 1234,
  "ip": "203.0.113.4",
  "registered": { "ip": "203.0.113.4", "status": "registered" }
}

registered.status is one of registered, already_registered or existing. Errors return 400 (invalid IP), 401 (missing/invalid key), 403 (key not active) or 502 (upstream registration failure with error, code, client_id, ip and hint fields).

How do I read my detection configuration?

GET /api/v1/config returns client-level settings, polled by detection servers roughly every 60 seconds:

{
  "clientId": 1234,
  "detectionSensitivity": 3,
  "maxDetectionMs": 8000,
  "updatedAt": "2026-08-01T12:00:00.000Z"
}

detectionSensitivity defaults to 3, maxDetectionMs defaults to 8000.

How do I read per-IP settings?

GET /api/v1/client-settings returns one entry per IP the client owns, plus a default entry. It is rate-limited to 20 requests per minute per key (429 with Retry-After).

{
  "version": 1722470400,
  "generated_at": "2026-08-28T09:00:00.000Z",
  "clients": {
    "default": { "max_detection_time": 8, "detection_mode": "balanced" },
    "203.0.113.4": {
      "max_detection_time": 6,
      "detection_mode": "aggressive",
      "stage_sensitivity": { "1k": 3, "32k": 10 }
    }
  }
}
Setting keyValues
max_detection_time0.5 to 10.0, clamped
detection_modeaggressive, balanced, conservative
greeting_graceboolean
immediate_detectionboolean
stage_resultsboolean
short_no_greetingboolean
stage_sensitivityoptional object, any subset of 1k, 2k, 3k, 4k, 8k, 12k, 16k, 24k, 32k mapped to an integer 1-10. 1 = most aggressive at that stage, 5 = current production, 10 = stage disabled. Omitted stages fall back to detection_mode.

stage_sensitivity is per-stage, not all-or-nothing: an explicit stage level wins over the detection_mode profile, which in turn wins over the shipped defaults. Levels carry no thresholds — the level-to-threshold ladder lives on the detection servers, so a retune keeps every level’s meaning stable.

Each key is omitted when NULL. Ownership is checked against firewall_production.client_ips; no rows returns 200 with an empty clients object.

How do I list my registered IPs?

GET /api/v1/ips returns the same rate limit (20/min) and the same per-IP settings keys as /api/v1/client-settings. Responses are never cached (Cache-Control: no-store).

{
  "client_id": 1234,
  "generated_at": "2026-08-28T09:00:00.000Z",
  "ips": [
    {
      "ip": "203.0.113.4",
      "status": "active",
      "settings": { "max_detection_time": 6, "detection_mode": "aggressive" }
    }
  ]
}

What is the install command per platform?

PlatformInstall command
ViciDial / ViciBoxcurl -fsSL https://download.amdy.io/installamd-v2.sh | bash -s <API_KEY>
Asterisk (custom dialer)curl -sL https://download.amdy.io/installamd-asterisk.sh | bash -s -- <API_KEY>
FreeSWITCHinstallamd-freeswitch.sh <API_KEY> --module /path/to/mod_audio_fork.so (no curl one-liner; module must be supplied)
IssabelNot verified in source. Contact us for integration access.

Every installer writes the key to /etc/amdy/api-key (mode 600) and calls POST /api/v1/ips/register with the host's detected public IP. On ViciBox, never chown to asterisk: Vicidial runs as root.

Frequently asked questions

Is there a wss:// endpoint?

Not verified in source. Only ws://api.amdy.io:2700 is documented in this repository. Contact us to confirm TLS availability for your network.

Can I revoke an API key over the public API?

No. Key management (create and revoke) is portal-only via /api/api-keys, authenticated by your NextAuth session. Revoke sets status='revoked'; the row is not deleted.

Can I delete a registered IP over the API?

No. IP removal is portal-only. The public API exposes register and read routes, not delete.

Is Issabel supported?

Not verified in source. Issabel is Asterisk-based, but AMDY ships no Issabel-specific installer or dialplan. Contact us for integration access.

What does /api/health return?

Platform-wide stats only: {"status":"operational|degraded","detections_7d":...,"human_pct_7d":...,"p95_latency_ms":...,"updated_at":"..."}. Cached for 5 minutes. No per-client data.

Ready to integrate?

Generate an API key from your AMDY.IO dashboard and run the installer for your platform. The free Sandbox plan includes 50,000 detections per month, no card.

Open the AMDY dashboard

Support

Questions about an endpoint not listed here? Email [email protected] with the route, the request you sent and the response you received.