Get started
Quickstart
Send your first notification in about five minutes, from a token to a tracked delivery.
You need an OAuth application (client id and secret) from your Nofy operator. Everything else below is self-service.
Base URL
Every request goes to https://nofy.encipher.co.tz. The original host,
https://ens.encipher.co.tz, serves the same API for existing
integrations.
Get a token
Exchange your client credentials for a Bearer token:
Terminalcurl -X POST https://nofy.encipher.co.tz/v1/oauth2/token/ \ -d grant_type=client_credentials \ -d client_id=YOUR_CLIENT_ID \ -d client_secret=YOUR_CLIENT_SECRETJSON{ "access_token": "abc123", "expires_in": 36000, "token_type": "Bearer", "scope": "read write" }Pass it as
Authorization: Bearer abc123on every call. Tokens carry no user: the API authorizes the client, and every send is attributed to it.Provision a tenant
A tenant is one of your customers. Sends to unknown tenants are rejected, so register each one first. Provisioning again is safe and returns the existing tenant:
Terminalcurl -X POST https://nofy.encipher.co.tz/v2/tenants \ -H "Authorization: Bearer abc123" \ -H "Content-Type: application/json" \ -d '{"external_id": "company-uuid-1", "name": "Acme Fuel"}'Send a message
One handoff with an
Idempotency-Keyheader, so a retried request never sends twice. Python has an SDK; every other language calls the same HTTP API:from ens_sdk import ENS # ENS_SERVER_URL, ENS_CLIENT_ID, ENS_CLIENT_SECRET, ENS_TENANT ens = ENS() ens.send_sms("+255700000000", "Shift closed at 18:00.")// Mint a token first: POST /v1/oauth2/token/ (grant_type=client_credentials) const res = await fetch("https://nofy.encipher.co.tz/v2/messages", { method: "POST", headers: { Authorization: `Bearer ${process.env.ENS_TOKEN}`, "Content-Type": "application/json", "Idempotency-Key": "notif-0001", }, body: JSON.stringify({ tenant: "company-uuid-1", event_type: "shift.closed", messages: [{ channel: "sms", recipients: ["255700000000"], content: { kind: "rendered", body: "Shift closed at 18:00." }, }], }), }); if (res.status !== 202) throw new Error(`ENS ${res.status}`); console.log(await res.json());<?php // Mint a token first: POST /v1/oauth2/token/ (grant_type=client_credentials) $ch = curl_init("https://nofy.encipher.co.tz/v2/messages"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ENS_TOKEN"), "Content-Type: application/json", "Idempotency-Key: notif-0001", ], CURLOPT_POSTFIELDS => json_encode([ "tenant" => "company-uuid-1", "event_type" => "shift.closed", "messages" => [[ "channel" => "sms", "recipients" => ["255700000000"], "content" => ["kind" => "rendered", "body" => "Shift closed at 18:00."], ]], ]), ]); $body = json_decode(curl_exec($ch), true); if (curl_getinfo($ch, CURLINFO_RESPONSE_CODE) !== 202) { throw new Exception("ENS send failed"); }// Mint a token first: POST /v1/oauth2/token/ (grant_type=client_credentials) body, _ := json.Marshal(map[string]any{ "tenant": "company-uuid-1", "event_type": "shift.closed", "messages": []any{map[string]any{ "channel": "sms", "recipients": []string{"255700000000"}, "content": map[string]string{"kind": "rendered", "body": "Shift closed at 18:00."}, }}, }) req, _ := http.NewRequest("POST", "https://nofy.encipher.co.tz/v2/messages", bytes.NewReader(body)) req.Header.Set("Authorization", "Bearer "+os.Getenv("ENS_TOKEN")) req.Header.Set("Content-Type", "application/json") req.Header.Set("Idempotency-Key", "notif-0001") res, err := http.DefaultClient.Do(req) if err != nil || res.StatusCode != 202 { log.Fatal("ENS send failed") }// Cargo.toml: reqwest { version = "0.12", features = ["json", "rustls-tls"] }, tokio, serde_json // Mint a token first: POST /v1/oauth2/token/ (grant_type=client_credentials) let res = reqwest::Client::new() .post("https://nofy.encipher.co.tz/v2/messages") .bearer_auth(std::env::var("ENS_TOKEN")?) .header("Idempotency-Key", "notif-0001") .json(&serde_json::json!({ "tenant": "company-uuid-1", "event_type": "shift.closed", "messages": [{ "channel": "sms", "recipients": ["255700000000"], "content": {"kind": "rendered", "body": "Shift closed at 18:00."} }] })) .send() .await?; if res.status() != 202 { anyhow::bail!("ENS send failed: {}", res.status()); }curl -X POST https://nofy.encipher.co.tz/v2/messages \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: notif-0001" \ -d '{"tenant": "company-uuid-1", "event_type": "shift.closed", "messages": [{"channel": "sms", "recipients": ["255700000000"], "content": {"kind": "rendered", "body": "Shift closed at 18:00."}}]}'A
202means accepted and queued, not delivered. The response lists one delivery per recipient, each starting aspending:JSON{ "reference": "shift-8841", "message_id": "vA51xTo874Y8", "deliveries": [ { "id": "dL91...", "channel": "sms", "recipient": "255700000000", "status": "pending" } ] }Track the delivery
Pull your deliveries, filtered by your own reference:
Terminalcurl "https://nofy.encipher.co.tz/v2/deliveries?reference=shift-8841" \ -H "Authorization: Bearer abc123"Or pass a
callback_urlon the send and receive a signed post as each delivery resolves.