Developers
HAMANI Marketing API
A versioned REST API (/api/v1) plus signed webhooks. Machine-readable spec: /api/v1/openapi.json. API access is included with your account — there is no separate API fee; message sends are billed at the same per-message rates as the app, and only when a message is accepted for sending by our delivery provider.
Authentication
Create keys in the app under API keys. Each key is scoped, shown once, and stored by us only as a hash. Send it on every request:
curl https://app.hamanimarketing.com.au/api/v1/wallet \ -H "Authorization: Bearer hm_live_…"
Scopes: campaigns:read · campaigns:write · contacts:read · contacts:write · wallet:read · sends:read · webhooks:manage. Keep keys secret — you are responsible for their storage; revoke immediately if one leaks.
Endpoints
| Method | Path | Scope | What it does |
|---|---|---|---|
| GET | /api/v1/campaigns | campaigns:read | List campaigns. Every campaign carries locale + direction; per-language step content carries its own direction. |
| POST | /api/v1/campaigns | campaigns:write | Create a DRAFT campaign. Activation is a separate call that runs the full Spam Act 2003 compliance gate. An unknown locale falls back to the default and sets locale_fallback:true — it never errors. Supports Idempotency-Key. |
| GET | /api/v1/campaigns/{id} | campaigns:read | Fetch one campaign. |
| PATCH | /api/v1/campaigns/{id} | campaigns:write | Partial update. A patch that would leave an active campaign non-compliant is refused with the reasons. |
| DELETE | /api/v1/campaigns/{id} | campaigns:write | Delete a campaign. |
| POST | /api/v1/campaigns/{id}/activate | campaigns:write | Activate — refused (422 with the reasons) unless sender identity and one-click unsubscribe are in place. Sends then dispatch on the schedule, inside the recipient's local send window, and debit only on an adapter-confirmed send. Supports Idempotency-Key. |
| GET | /api/v1/contacts | contacts:read | List contacts (preferred_language + direction included). |
| POST | /api/v1/contacts | contacts:write | Create/upsert a contact by email. consent_basis (how the person gave permission) is REQUIRED — the Spam Act consent gate. Supports Idempotency-Key. |
| GET | /api/v1/contacts/{id} | contacts:read | Fetch one contact. |
| PATCH | /api/v1/contacts/{id} | contacts:write | Update a contact. The API can set an opt-out but can never UNDO one — re-consent must come from the person. |
| DELETE | /api/v1/contacts/{id} | contacts:write | Delete a contact. |
| GET | /api/v1/wallet | wallet:read | Available prepaid balance + validity. Read-only: the API has no spend surface; sends debit only on an adapter-confirmed send (provider acceptance). |
| GET | /api/v1/sends | sends:read | Send statuses from the audit records: delivered (the only billed status), queued, failed (auto-retried, never charged), simulated, suppressed. Filter with ?campaign_id=. |
| POST | /api/v1/one-click | campaigns:write | One-click campaign: without approve → proposal + compliance warnings + Waste Filter savings; with approve → full gate, then create + activate. Standing waste flags need override_waste:true (logged). Supports Idempotency-Key. |
| GET | /api/v1/webhooks | webhooks:manage | List webhook endpoints (signing secrets masked). |
| POST | /api/v1/webhooks | webhooks:manage | Register an endpoint (https). The signing secret is returned ONCE — verify hamani-signature (HMAC-SHA256 of "timestamp.body") on every delivery. Supports Idempotency-Key. |
| DELETE | /api/v1/webhooks/{id} | webhooks:manage | Remove a webhook endpoint. |
Example — create and activate a campaign
# 1 · create a draft (Idempotency-Key makes retries safe)
curl -X POST https://app.hamanimarketing.com.au/api/v1/campaigns \
-H "Authorization: Bearer hm_live_…" \
-H "Idempotency-Key: create-winter-2026" \
-H "Content-Type: application/json" \
-d '{"name":"Winter specials","locale":"ne",
"sender_name":"Asha's Café",
"sender_identity":"Asha's Café, 12 Main St, Parramatta NSW",
"steps":[{"id":"s1","channel":"email",
"subject":"Hi {{first_name}}",
"body":"Our winter menu is here.\n\n{{unsubscribe}}",
"delay_hours":0}]}'
# 2 · activate (runs the Spam Act compliance gate — 422 with reasons if not ready)
curl -X POST https://app.hamanimarketing.com.au/api/v1/campaigns/CAMPAIGN_ID/activate \
-H "Authorization: Bearer hm_live_…"Webhooks
Register endpoints via POST /api/v1/webhooks. Events: send.delivered · send.failed · campaign.completed · wallet.low · wallet.expiring · contact.unsubscribed. Every delivery is signed — verify before trusting:
// header: hamani-signature: t=1780000000,v1=hex const [t, v1] = parseHeader(req.headers["hamani-signature"]); const expected = hmacSha256Hex(yourEndpointSecret, t + "." + rawBody); if (v1 !== expected) reject(); // and check t is recent
Failed deliveries retry with exponential backoff (up to 8 attempts). An endpoint that fails 10 times in a row is switched off so retries can’t pile up — fix it and register it again. Return any 2xx to acknowledge.
Rate limits & idempotency
300 requests per 5 minutes per key (429 with Retry-After beyond that). POST endpoints accept an Idempotency-Key header: a retry with the same key returns the stored result and never acts twice.
The rules the API enforces for you
- No sending without a recorded consent source (Spam Act 2003) — contacts require
consent_basis. - Nothing activates without sender identity and one-click unsubscribe — the same gate as the app.
- Unsubscribes are permanent from your side: the API cannot opt someone back in.
- Sends dispatch inside the recipient’s local 9am–8pm window and debit only when a message is accepted for sending by our provider (queued/failed/simulated are never charged).
- Multiple languages with automatic direction handling (right-to-left included); unknown locales fall back — they never error.