Skip to content

Release history

Hail.so releases

All releases

23 shown

No immediate action
v0.18.0 New feature

CLI transcript overhaul

No immediate action
v0.17.0 New feature

Self-opening agent + AI disclosure

No immediate action
v0.15.0 New feature

Multi‑language calls + VoiceBot AMD/DTMF + Webhook events

No immediate action
v0.16.0 New feature

Multi‑language calls + realism

No immediate action
v0.14.0 New feature

SMS pricing, contacts CLI, multi‑country numbers

Review required
v0.13.0 Mixed
Auth RBAC

voicebot tools, API caps, abuse, contacts, web UI

No immediate action
v0.12.1 New feature

sender-ID + idempotent acquire

No immediate action
v0.12.0 New feature

Email attachments + disclosure

Review required
v0.11.0 New feature
Auth RBAC

SMS compliance & provider validation

Review required
v0.10.0 Breaking risk
Auth RBAC

SMS outbound addition

Review required
v0.9.1 Bug fix
Auth Breaking upgrade

API error handling + consent enforcement

v0.9.0 Breaking risk
⚠ Upgrade required
  • Pin the SDK to `hail-sdk>=0.6.0` (PyPI: hail‑sdk==0.6.0) and the CLI to `cli-v0.9.0`+ before upgrading against an API running this change.
  • Update integration codepaths that invoke `POST /calls` or `POST /emails` to include `recipient_consent:true` (or the real attestation).
Breaking changes
  • `POST /calls` and `POST /emails` now require a top‑level boolean field `recipient_consent`; missing or false yields HTTP 422. Existing clients must add this flag (CLI `--recipient-consent`, SDK `recipient_consent=` kwarg, MCP `recipient_consent` param).
Notable features
  • Outbound voice calls and emails prepend a fixed AI‑disclosure line/message.
  • Global suppression list with one‑click email unsubscribe (`List-Unsubscribe` headers) plus pre‑send compliance gate for suppressed contacts, premium‑rate destinations, and org rate limits.
Full changelog

Changelog

All notable changes to Hail are documented here. The format is based on Keep a Changelog, and Hail adheres to Semantic Versioning.

[0.9.0] — 2026-07-07

Breaking change. Component versions cut alongside this release:
sdk-v0.6.0 (PyPI: hail-sdk==0.6.0), cli-v0.9.0 (Homebrew + GitHub Releases).

  • POST /calls and POST /emails now require a recipient_consent: bool
    field (rejected with 422 if missing or false). This attests that the
    caller has obtained the lawful consent required to contact the recipient —
    Hail does not verify consent itself. Optional consent_source and
    consent_obtained_at fields record how/when consent was obtained; a
    required non-empty consent_source is enforced when the new optional
    message_type: "marketing" field is set (default remains
    "informational").
    • Every existing integration must add recipient_consent: true (or the
      real attestation) to its calls/emails, or requests will start failing
      with 422 the moment the API deploys this change.
      The CLI (--recipient-consent),
      SDK (recipient_consent= kwarg), and MCP tools (recipient_consent
      param) all already require and forward it as of this release — pin to
      hail-sdk>=0.6.0 and cli-v0.9.0+ before or at the same time as
      upgrading against a Hail API instance running this change.
  • Outbound voice calls now open with a fixed, non-configurable AI-disclosure
    line spoken before anything else; outbound email carries an equivalent
    disclosure appended at send time, outside the caller-authored body.
  • New global suppression list + one-click email unsubscribe
    (List-Unsubscribe/List-Unsubscribe-Post headers); a pre-send
    compliance gate blocks suppressed recipients, premium-rate destinations,
    and per-org rate limits before any call or email goes out.
  • New data-retention purge job and DSAR (lookup/export/delete) tooling for
    recipient data requests.
  • /legal/* pages (Terms of Use, AUP, Privacy Policy, DPA, sub-processors,
    Cookie Policy) published on hail-website.

[0.8.1] — 2026-07-02

Internal consolidation release. cli-v0.8.1 cut alongside; the SDK is
unchanged and stays at hail-sdk==0.5.0.

  • One shared cursor-pagination helper now backs every paginated API route
    (was seven hand-copies) and one generic page-walker backs all three CLI
    list commands. Wire contract unchanged, with one edge unified: an
    empty-string cursor query param is now ignored everywhere instead of
    returning 400 on some routes.
  • hail email events --all now emits the same walked-items stderr warning
    as call list --all and email list --all.

[0.8.0] — 2026-07-02

Component versions cut alongside this umbrella release:
sdk-v0.5.0 (PyPI: hail-sdk==0.5.0), cli-v0.8.0 (Homebrew + GitHub Releases).

  • GET /emails/{id}/events is now cursor-paginated (cursor, limit 1..1000,
    next_cursor in the response). hail email events gains
    --cursor/--limit/--all; SDK emails.events() and MCP get_email_events
    accept cursor/limit. Existing callers are unaffected (default limit 100).

[0.7.0] — 2026-07-02

Email deliverability milestone. Every outbound email now has a tracked
lifecycle — delivered, delayed, bounced, complained, rejected, opened,
clicked — visible per message, aggregated per account, and pushed to
customer webhooks.

Component versions cut alongside this umbrella release:
sdk-v0.4.0 (PyPI: hail-sdk==0.4.0), cli-v0.7.0 (Homebrew + GitHub Releases).

Deliverability tracking

  • New email_events table (append-only, migration 0020); outbound sends write a synthetic sent event, and SES configuration-set events (Delivery, Bounce, Complaint, Reject, DeliveryDelay, Open, Click) are ingested via SNS → ingest Lambda → POST /internal/ses-events. Duplicate SNS deliveries are absorbed by a dedup constraint; status transitions are guarded so terminal states never regress. emails.status gains delivered.
  • GET /emails/{id}/events — per-email lifecycle timeline. GET /emails/{id} gains last_event_at.
  • GET /emails/stats?from=&to=&bucket=hour|day — account-level counts, rates (delivery, hard-bounce, complaint, unique open/click), and a zero-filled time series.
  • Email events join the unified GET /events stream (source: "email", id=email:<uuid> filter).
  • Webhooks: email.delivered, email.delivery_delayed, email.opened, email.clicked are new; email.bounced and email.complained now actually fire.
  • CLI: hail email events <id>, hail email stats. SDK: client.emails.events(), client.emails.stats(). MCP: get_email_events, get_email_stats.
  • Infra: SES configuration set (HAIL_SES_CONFIGURATION_SET, Terraform default hail-events), SNS topic with explicit SES publish policy, subscription DLQ. Open/click tracking uses the default SES tracking domain in v1.

[0.6.0] — 2026-06-28

Custom sender domains milestone. Tenants can now send and receive on their own
domain, with DKIM and a custom MAIL FROM configured automatically.

Custom sender domains — send and receive on your own domain

  • Register a tenant-controlled domain: POST /email-domains with kind=custom (or hail email domain register --kind custom --domain acme.com). Hail calls SES CreateEmailIdentity and auto-configures a custom MAIL FROM on send.<domain> via PutEmailIdentityMailFromAttributes — no AWS-console step for the tenant.
  • The registration response returns the full DNS record set to publish: three DKIM _domainkey CNAMEs plus the MAIL FROM send.<domain> MX (feedback-smtp.<region>.amazonses.com) and SPF TXT (v=spf1 include:amazonses.com ~all). DnsRecord gains an optional priority for MX rows.
  • POST /email-domains/{id}/verify re-polls SES for both DKIM and MAIL FROM, surfacing mail_from_status alongside verification_status. On-demand only — clients/console re-poll; there is no background poller.
  • Receive inbound mail on verified custom domains. Inbound is matched by the owning identity, so a message to two of an org's domains yields one inbound row + webhook per matched domain, with the domain name on the event payload.
  • New IAM grant ses:PutEmailIdentityMailFromAttributes for the production identity (infra/terraform/iam.tf) — required for the custom MAIL FROM call.

Auth

  • The managed console now authenticates to the API with a short-lived Better Auth session JWT (carrying sub and the session activeOrganizationId) rather than a cached per-browser API key. The API resolves the request's organization from the active-org claim, validated against membership — closing a cross-tenant write path.

0.5.0 — 2026-06-12

Inbound email milestone. Hail can now receive mail at hail-mail addresses,
forward it, and fire webhooks on receipt and on bounce/complaint events.

Component versions cut alongside this umbrella release:
sdk-v0.3.0 (PyPI: hail-sdk==0.3.0), cli-v0.5.0 (Homebrew + GitHub Releases).

Inbound email

  • SES Receipt Rule → S3 (raw MIME) + Lambda (HMAC-signed JSON) → POST /internal/ses-events ingest pipeline.
  • Email.direction='inbound' rows with message_id, in_reply_to, references_ids, raw_s3_key, and SES SPF/DKIM/DMARC/spam/virus verdicts persisted.
  • email_attachments table — MIME attachments stored as separate S3 objects under attachments/<email_id>/<att_id>.
  • Threading via In-Reply-To / References chains.
  • Spam/virus FAIL verdicts persist the row but suppress forwarding and webhook fan-out.
  • Idempotency via a partial unique index on (organization_id, message_id) WHERE direction='inbound' — duplicate SES deliveries short-circuit.

Forwarding

  • Per-domain forward_to list on email_domains. Inbound matches trigger one outbound row per target through the existing send loop.
  • Header-rewrite forwarding (Cloudflare Email Routing-style): From: = forwarder+<org>@<base_domain> (SPF/DKIM aligned), Reply-To: = original sender, References: preserves threading.
  • Loop guards: 3-hop max via X-Hail-Forward-Hops, refuse targets on HAIL_MAIL_BASE_DOMAIN, per-domain rate cap (default 200/hour, override via forward_rate_per_hour).

Webhooks

  • Org-wide webhook_subscriptions (multi-event firehose) + per-domain email_domains.webhook_url (single-target ergonomic). Both fire independently.
  • Background WebhookWorker polls webhook_deliveries with FOR UPDATE SKIP LOCKED, signs (X-Hail-Signature: t=<unix>,v1=<hmac-sha256>), POSTs via httpx with a private-network guard.
  • Retry ladder: 0s, 30s, 2m, 10m, 1h, 6h, 24h. After the last slot the row is dead; 50 consecutive deads auto-disable the subscription.
  • POST /webhooks/{id}/deliveries/{did}/redeliver to replay a dead row.
  • Webhook secrets are Fernet-encrypted at rest (HAIL_WEBHOOK_SECRET_KEY), so the worker survives an API restart without secret rotation.

Schema rename: sender_domainsemail_domains

  • The table now carries both directions, so the historical "sender" name no longer fits. Renames the table, indexes, trigger, CHECK constraints, and the FK column on emails.
  • Wire format: routes moved from /sender-domains/email-domains; audit-action strings from sender_domain.*email_domain.*; pydantic schemas SenderDomain*EmailDomain*.

API

  • GET /emails?direction=inbound|outbound filter.
  • GET /emails/{id}/raw and GET /emails/{id}/attachments/{aid} — 302 redirect to presigned S3 URLs (5-min TTL).
  • PATCH /email-domains/{id} accepts inbound action fields (inbound_enabled, forward_to, webhook_url, forward_rate_per_hour). Setting webhook_url mints a secret returned once.
  • POST /email-domains/{id}/rotate-webhook-secret rotates and returns the new plaintext once.
  • Full /webhooks CRUD + /rotate-secret + /deliveries + /deliveries/{id}/redeliver.

CLI (cli-v0.5.0)

  • New hail webhooks subcommand tree (create, list, deliveries, redeliver).
  • hail email list --direction inbound filter.
  • hail email sender-domain renamed to hail email email-domain (alias ed); the legacy sd / sender-domain aliases are removed — breaking for users with scripts hitting the old name.
  • Short-ID prefix resolution on every UUID-taking subcommand (was call status only): email get, email-domain get/verify/delete, webhooks deliveries, webhooks redeliver all accept the 4+ char hex prefix shown in list output, mirroring git rev-parse's short-hash UX.

SDK (sdk-v0.3.0)

  • client.webhooks.* resource added.
  • client.emails.list(direction=...) parameter added.
  • client.email_domains.patch() accepts inbound action fields; client.email_domains.rotate_webhook_secret(id) added.
  • SenderDomain* back-compat aliases removed (deprecated since sdk-v0.2.0) — breaking for users importing the legacy names.

Infrastructure

  • Terraform module under infra/terraform/ provisions S3 (raw + attachments + lifecycle), SES Receipt Rule + Rule Set, Lambda + IAM + log group, and emits the MX record + the set-active-receipt-rule-set activation command.
  • Stdlib-only infra/ses-ingest-lambda/handler.py bridges SES events into the HMAC-signed POST.
  • Three migrations: 0006 (rename + auxiliary objects), 0007 (Email inbound columns + email_attachments + email_domains action columns + idempotency partial unique index), 0008 (webhook_subscriptions + webhook_deliveries). See docs/operations.md → "Inbound email rollout" for the staged deploy sequence.
  • InboundProvider interface in core/hailhq/core/providers/email/inbound/ with SesInboundProvider shipping and SmtpInboundProvider stubbed for a future cloud-agnostic milestone.
  • Operator-facing env vars (HAIL_INBOUND_EMAIL_NAME_PREFIX, HAIL_IAM_USER_NAME) are required in .env — terragrunt fails fast on missing values instead of silently using a hardcoded default. The inbound bucket name is now a single source of truth (Settings.hail_inbound_bucket is a computed ${prefix}-raw), so Terraform and the API can't drift.

Deferred to next milestone

  • Custom-domain inbound (tenant MX → SES) — schema is ready; the operator-side flow isn't.
  • inbound_routes table for per-mailbox routing within custom domains.
  • SmtpInboundProvider implementation (cloud-agnostic / OSS-only path).
  • Reply-composition endpoint (POST /emails/{id}/reply).
  • MCP push transport for inbound events.

0.1.0 — 2026-04-30

First public release. Outbound phone calls for AI agents, end-to-end.

Phone calls

  • Outbound calls via Twilio SIP through LiveKit Cloud.
  • POST /calls, GET /calls/{id}, and GET /calls with cursor pagination.
  • Per-org API-key auth with audit logging on every authenticated request.
  • Idempotency-Key support on POST /calls (24h TTL).

Voice pipeline

  • Deepgram STT.
  • Cartesia TTS (primary) with ElevenLabs fallback.
  • Silero VAD (prewarmed once per worker process).
  • LiveKit turn-detector for end-of-utterance detection.
  • LLM system-prompt mode with OpenAI to Gemini to Anthropic fallback chain.
  • LLM BYO-endpoint mode for any OpenAI chat-completions-compatible endpoint.
  • Per-turn call_events rows for transcript reconstruction.

Distribution

  • OpenAPI 3.1 spec at openapi/openapi.yaml as the source of truth.
  • hail CLI binary published via GitHub Releases (darwin and linux, amd64 and arm64) and a Homebrew tap.
  • Remote MCP server bundled with every Hail deploy at /sse, exposing a place_call tool.
  • hail-sdk on PyPI (imports as hail) with Client.calls.create / get / list.

Infrastructure

  • docker compose up brings up api, voicebot, mcp, and Postgres.
  • Alembic migrations for the v1 schema.
  • CI runs lint, pytest with a Postgres service container, Go build and test, and a docker-compose build smoke.

Deferred to v1.x

  • LiveKit Egress recording (recording.py is stubbed; Call.recording_s3_key is always NULL).
  • idempotency_keys GC sweeper and in-flight reaper.
  • Inbound calls (LIVEKIT_SIP_INBOUND_TRUNK_ID is reserved in env but unused).
  • SMS channel (Twilio outbound and inbound).
  • Email channel (AWS SES outbound and inbound).
  • CallEvent dedupe across voicebot redispatch.
No immediate action
v0.8.1 Maintenance

Routine maintenance and dependency updates.

No immediate action
v0.8.0 New feature

Cursor pagination + wider amount field

No immediate action
v0.6.1 Bug fix

Root help lists all subcommands

Review required
v0.6.0 Breaking risk
Auth

CLI DX, email send/receive, webhooks, billing, MCP tools

Review required
v0.5.0 Breaking risk
Auth RBAC

email, mcp, costs, cli, infra

No immediate action
v0.4.0 Breaking risk

costs, email, web, cli, core

Review required
v0.3.0 Breaking risk
Auth

CLI prod default; pool soft-cap; compose DB split

No immediate action
v0.2.2 Mixed

Vercel fixes + UUID API + billing usage

Config change
v0.2.1 Breaking risk
Auth

Login defaults + flag rename

Config change
v0.2.0 New feature
Auth

hail login + CLI tag fix + config split

No immediate action
v0.1.0 New feature

Outbound calls + Voice pipeline

Beta — feedback welcome: [email protected]