eSMS AfricaeSMS Africa
API ReferenceSMS API

Contacts and groups

The contact book (everyone across your groups, by phone) and contact groups, with custom fields, opt-out status and history, search and export.

Contacts live in groups (the older "contact lists"; /v1/contact-lists keeps working). The contact book shows each person once, across all groups, addressed by E.164 phone (+256772123456; URL-encode the + as %2B if your client requires it). Custom fields are stored per contact and are available as merge fields when you send to a group. Keys need send to write and query to read.

Contact book

MethodPathDescription
GET/v1/contactsSearch: search (phone or name), group_id, opted_out (true / false), page, limit (max 200). Each item: phone, name, groups[], fields, opted_out, created_at.
POST/v1/contacts{phone, name?, fields?, group_ids: [...]} - add to groups, or update where it already exists (fields are merged).
GET/v1/contacts/{phone}Fields, groups, opted_out, opt_out details, opt_out_history (STOP / START events) and the 20 most recent messages.
PATCH/v1/contacts/{phone}{phone?, name?, fields?} - updates every row of that number; fields are merged.
DELETE/v1/contacts/{phone}Removes the number from every group (204).
GET/v1/contacts/exportAll contacts, or group_id=, as format=csv (default) or xlsx: phone, name, group, opted_out, custom fields, added_at.

A numeric path segment under 10 digits (/v1/contacts/123) addresses a single group row by id, as the older API did.

Local numbers like 0772... are read in your workspace default country (or your signup country). Invalid numbers are refused with the reason (not_a_number, invalid_number, missing_country_code, landline, ...).

Groups

MethodPathDescription
GET/v1/groupsList (q filters by name).
POST/v1/groups{name, tags?} - an empty group.
GET/v1/groups/{id}id, name, count, columns, tags.
PATCH/v1/groups/{id}{name?, tags?}.
DELETE/v1/groups/{id}Deletes the group and its rows.
GET/v1/groups/{id}/contactssearch, page, limit (max 500); each row has opted_out.
POST/v1/groups/{id}/contacts{contacts: [{phone, name?, fields?}], default_country?} - up to 10,000; returns added and rejected (with reasons, including duplicate).
DELETE/v1/groups/{id}/contacts/{phone}Remove one number from the group.

For large files use Imports and POST /v1/imports/{id}/save-to-group.

Opted-out numbers stay in your groups (so you can see their history) but are never messaged: every send and campaign checks the opt-out list, including right before each batch.

Example

curl -X POST https://sms.esmsafrica.io/v1/groups \
  -H "Authorization: Bearer $ESMS_API_KEY" -H "Content-Type: application/json" \
  -d '{"name": "VIP customers"}'

curl -X POST https://sms.esmsafrica.io/v1/groups/12/contacts \
  -H "Authorization: Bearer $ESMS_API_KEY" -H "Content-Type: application/json" \
  -d '{"default_country": "KE", "contacts": [{"phone": "0712345678", "name": "Ada", "fields": {"tier": "gold"}}]}'

curl "https://sms.esmsafrica.io/v1/contacts/%2B254712345678" -H "Authorization: Bearer $ESMS_API_KEY"
// SDK 1.2.0 or later
const g = await esms.groups.create("VIP customers");
await esms.groups.addContacts(g.id, [{ phone: "0712345678", name: "Ada", fields: { tier: "gold" } }], "KE");
const person = await esms.contacts.get("+254712345678");
await esms.contacts.update("+254712345678", { fields: { tier: "platinum" } });
# SDK 1.2.0 or later
g = esms.groups.create("VIP customers")
esms.groups.add_contacts(g["id"], [{"phone": "0712345678", "name": "Ada", "fields": {"tier": "gold"}}], default_country="KE")
person = esms.contacts.get("+254712345678")
esms.contacts.update("+254712345678", fields={"tier": "platinum"})
// SDK 1.2.0 or later
$g = $esms->groups->create('VIP customers');
$esms->groups->addContacts($g['id'], [['phone' => '0712345678', 'name' => 'Ada', 'fields' => ['tier' => 'gold']]], 'KE');
$person = $esms->contacts->get('+254712345678');
$esms->contacts->update('+254712345678', ['fields' => ['tier' => 'platinum']]);
// SDK 1.2.0 or later
g, _ := client.Groups.Create(ctx, "VIP customers", nil)
gid := int(g["id"].(float64))
client.Groups.AddContacts(ctx, gid, []esms.GroupContact{{Phone: "0712345678", Name: "Ada", Fields: map[string]string{"tier": "gold"}}}, "KE")
person, _ := client.Contacts.Get(ctx, "+254712345678")
client.Contacts.Update(ctx, "+254712345678", esms.JSON{"fields": map[string]string{"tier": "platinum"}})

On this page