Use Odit with Claude or ChatGPT
Odit API reference
Read your own Odit data from a script, a spreadsheet or anything that speaks HTTP, or have Odit read SMS text you send. It can't change anything.
The basics
- Every call goes to
https://api.odit.etwith an API key:Authorization: Bearer odit_sk_.... Make one under Connected apps on your account page. - An answer is
{ "success": true, "data": ... }. A refusal is{ "success": false, "error": { "code", "message" } }; branch oncode, showmessage. - Money is
{ "amount": "1250.00", "currency": "ETB" }. The amount is a decimal string, never a float, so nothing is lost on the way. - Times are ISO 8601 in UTC. A bare date in a query, like
2026-10-01, means that whole day in Addis time. - Each call reads one phone: the one the app has open, or the one you name with
device, an id from/v1/devices. Two phones often hold the same SMS, so nothing adds them together. - Long lists come a page at a time. Pass
nextCursorback ascursoruntil it is null. - You get 60 requests a minute and 5,000 a day, shared with anything you connected through Claude or ChatGPT. Past that the answer is a 429 with
Retry-After. - v1 only ever gains fields. The machine-readable version of this page is the OpenAPI document.
Try it: read an SMS
Paste a message the way your phone got it, and Odit answers with what it reads. Nothing is saved to your account. The call underneath is POST /v1/parse.
Endpoints
- GET /v1/devicesYour phones, and which one the app has loaded.
- GET /v1/transactionsTransactions on one phone, newest first.
- GET /v1/transactions/{id}One transaction.
- GET /v1/accountsYour accounts on one phone, with the balance each last stated.
- GET /v1/accounts/{id}/balancesEvery balance an account stated, newest first.
- GET /v1/peoplePeople and merchants on one phone, most money moved first.
- GET /v1/categoriesYour categories: the built-in ones and your own.
- GET /v1/goalsGoals on one phone, with progress as the Goals screen shows it.
- GET /v1/recurringsRecurring payments and income on one phone.
- GET /v1/packagesTelecom packages on one phone: data, minutes and SMS left.
- GET /v1/summaryMoney in, money out, fees and net for a period, counted as the dashboard counts them.
- POST /v1/parseRead SMS text you send, the way Odit reads a synced message. Nothing is saved to your account. One answer per message, in the order sent.
- Webhooks what Odit sends to your server
GET /v1/devices
Your phones, and which one the app has loaded.
Answer
{
"success": true,
"data": [
{
"id": "00000000-0000-0000-0000-000000000001",
"name": "Galaxy A15",
"loaded": true,
"createdAt": "2026-03-05T09:12:44.000Z"
}
]
}data is a list of Device.
idstringThe device id: pass it as
deviceto read this phone.namestring, or nullloadedtrue or falseThe device the app has loaded; calls without
deviceread this one.createdAtstringISO 8601, UTC.
GET /v1/transactions
Transactions on one phone, newest first.
Parameters
devicestring, optionalA device id from /v1/devices. Omitted: the device the app has loaded.
fromstring, optionalOn or after. A bare date means the start of that day, Addis time.
tostring, optionalOn or before. A bare date means the end of that day, Addis time.
direction"incoming" or "outgoing", optionalcategory"none" or integer, optionalA category id, or
nonefor uncategorized.accountinteger, optionalAn account id.
qstring, optionalText matched against the message, the other side and the note.
updatedSincestring, optionalOnly transactions that changed at or after this time, oldest change first: keep the last
updatedAtyou saw and pass it back to poll for changes. Deletions are not listed; webhooks send those.limitinteger, optional, default 50cursorstring, optionalnextCursorfrom the previous page.include"rawSms", optionalrawSmsadds the SMS text to each row.
Answer
{
"success": true,
"data": [
{
"id": 48210,
"device": "00000000-0000-0000-0000-000000000001",
"occurredAt": "2026-10-12T08:41:00.000Z",
"receivedAt": "2026-10-12T08:41:06.000Z",
"direction": "outgoing",
"type": "TRANSFER_CBE_TO_TB",
"amount": { "amount": "1250.00", "currency": "ETB" },
"fee": { "amount": "5.00", "currency": "ETB" },
"balanceAfter": { "amount": "18420.55", "currency": "ETB" },
"provider": { "key": "cbe", "name": "CBE" },
"account": { "id": 312, "name": "CBE (Salary)" },
"counterparty": {
"name": "Abebe Kebede",
"identifier": "251911000000",
"type": "TELEBIRR"
},
"category": { "id": 7, "name": "Family" },
"isInternalTransfer": false,
"isDebt": false,
"note": "For the house",
"reference": "FT0000000000",
"source": "sms",
"updatedAt": "2026-10-12T09:03:18.000Z"
}
],
"nextCursor": "WyIyMDI2LTEwLTEyIDA4OjQxOjAwKzAwIiw0ODIxMF0"
}data is a list of Transaction, with nextCursor for the next page.
idintegerdevicestringThe id of the device this SMS came in on.
occurredAtstringWhen the bank sent the message. ISO 8601, UTC.
receivedAtstringWhen Odit stored it. ISO 8601, UTC.
direction"incoming" or "outgoing"typestringThe kind of message the parser read, e.g. TRANSFER_OUT. A label, not a fixed list.
amountmoneyfeemoney, or nullbalanceAftermoney, or nullThe balance the message states, if any.
providerobject, or nullThe bank or wallet that sent the message.
keystringnamestring
accountobject, or nullThe user's own account the money moved through, when Odit linked one.
idintegernamestring
counterpartyobject, or nullThe other side, when the message names one.
namestring, or nullidentifierstring, or nullPhone or account number, unmasked.
typestring, or null
categoryobject, or nullidintegernamestring
isInternalTransfertrue or falseMoney moving between two of the user's own accounts.
isDebttrue or falsenotestring, or nullThe user's own note.
referencestring, or nullThe bank's transaction reference.
source"sms" or "email"updatedAtstringWhen this transaction last changed in a way the API shows: a new category, a note, a reparse that read it differently. ISO 8601, UTC. Before any change, when Odit stored it.
rawSmsstring, sometimes left outThe SMS text. Only with
include=rawSms, and never for email.
GET /v1/transactions/{id}
One transaction.
Parameters
idin the pathinclude"rawSms", optionalrawSmsadds the SMS text.
Answer
{
"success": true,
"data": {
"id": 48210,
"device": "00000000-0000-0000-0000-000000000001",
"occurredAt": "2026-10-12T08:41:00.000Z",
"receivedAt": "2026-10-12T08:41:06.000Z",
"direction": "outgoing",
"type": "TRANSFER_CBE_TO_TB",
"amount": { "amount": "1250.00", "currency": "ETB" },
"fee": { "amount": "5.00", "currency": "ETB" },
"balanceAfter": { "amount": "18420.55", "currency": "ETB" },
"provider": { "key": "cbe", "name": "CBE" },
"account": { "id": 312, "name": "CBE (Salary)" },
"counterparty": {
"name": "Abebe Kebede",
"identifier": "251911000000",
"type": "TELEBIRR"
},
"category": { "id": 7, "name": "Family" },
"isInternalTransfer": false,
"isDebt": false,
"note": "For the house",
"reference": "FT0000000000",
"source": "sms",
"updatedAt": "2026-10-12T09:03:18.000Z"
}
}data is a Transaction.
idintegerdevicestringThe id of the device this SMS came in on.
occurredAtstringWhen the bank sent the message. ISO 8601, UTC.
receivedAtstringWhen Odit stored it. ISO 8601, UTC.
direction"incoming" or "outgoing"typestringThe kind of message the parser read, e.g. TRANSFER_OUT. A label, not a fixed list.
amountmoneyfeemoney, or nullbalanceAftermoney, or nullThe balance the message states, if any.
providerobject, or nullThe bank or wallet that sent the message.
keystringnamestring
accountobject, or nullThe user's own account the money moved through, when Odit linked one.
idintegernamestring
counterpartyobject, or nullThe other side, when the message names one.
namestring, or nullidentifierstring, or nullPhone or account number, unmasked.
typestring, or null
categoryobject, or nullidintegernamestring
isInternalTransfertrue or falseMoney moving between two of the user's own accounts.
isDebttrue or falsenotestring, or nullThe user's own note.
referencestring, or nullThe bank's transaction reference.
source"sms" or "email"updatedAtstringWhen this transaction last changed in a way the API shows: a new category, a note, a reparse that read it differently. ISO 8601, UTC. Before any change, when Odit stored it.
rawSmsstring, sometimes left outThe SMS text. Only with
include=rawSms, and never for email.
GET /v1/accounts
Your accounts on one phone, with the balance each last stated.
Parameters
devicestring, optionalA device id from /v1/devices. Omitted: the device the app has loaded.
Answer
{
"success": true,
"data": [
{
"id": 312,
"name": "CBE (Salary)",
"provider": { "key": "cbe", "name": "CBE" },
"label": "Salary",
"visibility": "visible",
"balance": { "amount": "18420.55", "currency": "ETB" },
"balanceAsOf": "2026-10-12T08:41:00.000Z",
"isDormant": false,
"isPrimary": true
}
]
}data is a list of Account.
idintegerThe account's id. A transaction's
account.idis one of these.namestringThe bank or wallet, with your own label: "telebirr (Main)".
providerobjectkeystringnamestring
labelstring, or nullThe label you gave it in the app.
visibility"visible" or "soft_hidden"soft_hiddenis cold storage: real money the app leaves out of everyday totals. Accounts you hid outright are not listed.balancemoney, or nullThe latest balance the account's own SMS stated, carried forward through later transactions. Null until one is seen.
balanceAsOfstring, or nullWhen that balance was last stated. ISO 8601, UTC.
isDormanttrue or falseMarked dormant in the app: its balance is not counted in totals.
isPrimarytrue or false
GET /v1/accounts/{id}/balances
Every balance an account stated, newest first.
Parameters
idin the pathfromstring, optionalOn or after. A bare date means that whole day, Addis time.
tostring, optionalOn or before. A bare date means that whole day, Addis time.
limitinteger, optional, default 100cursorstring, optionalnextCursorfrom the previous page.
Answer
{
"success": true,
"data": [
{
"at": "2026-10-12T08:41:00.000Z",
"balance": { "amount": "18420.55", "currency": "ETB" },
"transactionId": 48210
}
],
"nextCursor": "WyIyMDI2LTEwLTEyIDA4OjQxOjAwKzAwIiw0ODIxMF0"
}data is a list of BalanceReading, with nextCursor for the next page.
atstringWhen the SMS stating it was sent. ISO 8601, UTC.
balancemoneytransactionIdintegerThe transaction whose SMS stated this balance.
GET /v1/people
People and merchants on one phone, most money moved first.
Parameters
devicestring, optionalA device id from /v1/devices. Omitted: the device the app has loaded.
qstring, optionalText matched against the name.
limitinteger, optional, default 50cursorstring, optionalnextCursorfrom the previous page.
Answer
{
"success": true,
"data": [
{
"id": 905,
"name": "Abebe Kebede",
"sent": { "amount": "23500.00", "currency": "ETB" },
"received": { "amount": "4000.00", "currency": "ETB" },
"transactionCount": 18,
"isFavorite": true,
"tags": [ "family" ]
}
],
"nextCursor": "WyIyMDI2LTEwLTEyIDA4OjQxOjAwKzAwIiw0ODIxMF0"
}data is a list of Person, with nextCursor for the next page.
idintegernamestringsentmoneyAll time, as the People screen counts it.
receivedmoneyAll time, as the People screen counts it.
transactionCountintegerisFavoritetrue or falsetagslist of string
GET /v1/categories
Your categories: the built-in ones and your own.
Answer
{
"success": true,
"data": [
{
"id": 7,
"name": "Family",
"parentId": null,
"type": "BOTH",
"custom": false,
"hidden": false,
"color": "#3B82F6",
"icon": "users-three"
}
]
}data is a list of Category.
idintegerA transaction's
category.idis one of these.namestringparentIdinteger, or nullSet on a subcategory.
type"IN" or "OUT" or "BOTH" or "NEUTRAL"What money it's for. A subcategory takes its parent's type.
customtrue or falseMade by you, rather than one of the built-in categories.
hiddentrue or falseA built-in category you hid from your picker. Transactions already in it keep it.
colorstringiconstring, or null
GET /v1/goals
Goals on one phone, with progress as the Goals screen shows it.
Parameters
devicestring, optionalA device id from /v1/devices. Omitted: the device the app has loaded.
Answer
{
"success": true,
"data": [
{
"id": 41,
"name": "Emergency fund",
"type": "savings",
"timeframe": "open_ended",
"status": "active",
"target": { "amount": "50000.00", "currency": "ETB" },
"current": { "amount": "18420.55", "currency": "ETB" },
"progress": 36.8,
"startsAt": "2026-07-01T00:00:00.000Z",
"endsAt": null,
"isAchieved": false,
"achievedAt": null
}
]
}data is a list of Goal.
idintegernamestringtype"savings" or "spending_limit" or "income_target" or "balance_target"timeframe"one_time" or "daily" or "weekly" or "monthly" or "quarterly" or "yearly" or "open_ended"status"active" or "paused" or "archived"targetmoneycurrentmoneyWhere the goal stands now, as the Goals screen computes it.
progressnumberPercent of the target, 0 to 100, as the Goals screen shows it.
startsAtstringISO 8601, UTC.
endsAtstring, or nullISO 8601, UTC. Null when open ended.
isAchievedtrue or falseachievedAtstring, or null
GET /v1/recurrings
Recurring payments and income on one phone.
Parameters
devicestring, optionalA device id from /v1/devices. Omitted: the device the app has loaded.
Answer
{
"success": true,
"data": [
{
"id": 12,
"name": "Rent",
"direction": "outgoing",
"amount": { "amount": "15000.00", "currency": "ETB" },
"frequency": "monthly",
"status": "active",
"next": {
"expectedAt": "2026-10-31T21:00:00.000Z",
"status": "upcoming"
},
"lastOccurredAt": "2026-10-01T06:15:00.000Z"
}
]
}data is a list of Recurring.
idintegernamestringdirection"incoming" or "outgoing"amountmoneyfrequency"daily" or "weekly" or "biweekly" or "monthly" or "quarterly" or "yearly"status"active" or "paused" or "completed"nextobject, or nullThe next payment Odit is waiting for. Null when none is open.
expectedAtstringISO 8601, UTC.
statusstringWhether it is still expected, or already late.
lastOccurredAtstring, or nullISO 8601, UTC.
GET /v1/packages
Telecom packages on one phone: data, minutes and SMS left.
Parameters
devicestring, optionalA device id from /v1/devices. Omitted: the device the app has loaded.
Answer
{
"success": true,
"data": {
"asOf": "2026-10-12T09:00:00.000Z",
"packages": [
{
"provider": "ethiotelecom",
"name": "Monthly 12GB + 100 min",
"expiresAt": "2026-11-03T20:59:59.000Z",
"addedAt": "2026-10-04T07:20:00.000Z",
"parts": [
{
"kind": "data",
"unit": "MB",
"total": 12288,
"left": 7340.5,
"leftAt": "2026-10-12T08:55:00.000Z",
"unlimited": false,
"note": null
},
{
"kind": "voice",
"unit": "min",
"total": 100,
"left": 64,
"leftAt": "2026-10-12T08:55:00.000Z",
"unlimited": false,
"note": null
}
]
}
],
"totals": {
"dataMb": 7340.5,
"restrictedDataMb": 0,
"unlimitedData": false,
"voiceMin": 64,
"onNetVoiceMin": 0,
"sms": 0
},
"recentlyEnded": []
}
}data is a Packages.
asOfstringISO 8601, UTC.
packageslist of objectActive packages, soonest expiry first.
providerstringThe telecom:
ethiotelecomorsafaricom.namestringexpiresAtstringISO 8601, UTC.
addedAtstring, or nullISO 8601, UTC.
partslist of objectkind"data" or "voice" or "sms"unit"MB" or "min" or "sms"totalnumber, or nullWhat the package gave. Null when unknown or unlimited.
leftnumber, or nullWhat is left, as of
leftAt.leftAtstring, or nullISO 8601, UTC.
unlimitedtrue or falsenotestring, or nullWhat limits it:
night,bonus,appsoron-net.
totalsobjectdataMbnumberGeneral data left. App-only and night data are apart.
restrictedDataMbnumberunlimitedDatatrue or falsevoiceMinnumberMinutes that call anyone.
onNetVoiceMinnumbersmsnumber
recentlyEndedlist of objectPackages that ended in the last week, newest first.
providerstringnamestringexpiresAtstringendedAtstring
GET /v1/summary
Money in, money out, fees and net for a period, counted as the dashboard counts them.
Parameters
devicestring, optionalA device id from /v1/devices. Omitted: the device the app has loaded.
fromstring, optionalOn or after. A bare date means the start of that day, Addis time. Omitted: the first of this month.
tostring, optionalOn or before. A bare date means the end of that day, Addis time. Omitted: now.
Answer
{
"success": true,
"data": {
"from": "2026-09-30T21:00:00.000Z",
"to": "2026-10-31T20:59:59.999Z",
"moneyIn": { "amount": "42000.00", "currency": "ETB" },
"moneyOut": { "amount": "31250.40", "currency": "ETB" },
"fees": { "amount": "118.75", "currency": "ETB" },
"net": { "amount": "10630.85", "currency": "ETB" },
"transactionCount": 64,
"internalTransfers": {
"in": { "amount": "5000.00", "currency": "ETB" },
"out": { "amount": "5000.00", "currency": "ETB" }
},
"unclassifiedCount": 0
}
}data is a Summary.
fromstringISO 8601, UTC.
tostringISO 8601, UTC.
moneyInmoneymoneyOutmoneySpending, fees not included.
feesmoneyEvery fee in the period, internal transfers included.
netmoneyMoney in minus money out and fees. Can be negative.
transactionCountintegerinternalTransfersobjectMoney moved between your own accounts. Not counted above.
inmoneyoutmoney
unclassifiedCountintegerRows Odit could not tell the direction of. Not counted above.
POST /v1/parse
Read SMS text you send, the way Odit reads a synced message. Nothing is saved to your account. One answer per message, in the order sent. Try it with your own key. Which sender is which provider is under Senders.
Body
{
"messages": [
{
"id": "my-sms-1",
"sender": "127",
"body": "Dear Hana\nYou have transferred ETB 250.00 to Abebe Kebede (2519****0000) on 12/10/2026 11:41:00. Your transaction number is CAB0000000. The service fee is ETB 0.87 and 15% VAT on the service fee is ETB 0.13. Your current E-Money Account balance is ETB 1,830.55. To download your payment information please click this link: https://transactioninfo.ethiotelecom.et/receipt/CAB0000000.\n\nThank you for using telebirr\nEthio telecom"
}
]
}JSON, with Content-Type: application/json.
messageslist of objectidstring, optionalYour own id for the message. Sent back as is, so you can match results.
senderstringWho sent the SMS, as the phone shows it: "127", "CBE", "DashenBank".
bodystringThe SMS text, exactly as it arrived.
providerstring, optionalA provider key such as
telebirrorcbe. Reads the message as that provider and ignoressender. For a sender Odit does not know.
Answer
{
"success": true,
"data": [
{
"id": "my-sms-1",
"status": "parsed",
"provider": { "key": "telebirr", "name": "Telebirr" },
"type": "TRANSFER_TB_FROM_TB",
"direction": "outgoing",
"amount": { "amount": "250.00", "currency": "ETB" },
"fee": { "amount": "1.00", "currency": "ETB" },
"balanceAfter": { "amount": "1830.55", "currency": "ETB" },
"participants": [
{
"role": "sender",
"name": "Hana",
"identifier": null,
"type": "TELEBIRR"
},
{
"role": "receiver",
"name": "Abebe Kebede",
"identifier": "2519****0000",
"type": "TELEBIRR"
}
],
"reference": "CAB0000000",
"receiptUrl": "https://transactioninfo.ethiotelecom.et/receipt/CAB0000000"
}
]
}data is a list of ParsedMessage.
idstring, or nullThe
idyou sent, or null.status"parsed" or "not_recognized" or "unsupported_sender"parsed: Odit read the message.not_recognized: the sender is a bank or wallet Odit reads, but not this kind of message.unsupported_sender: Odit does not read this sender.providerobject, or nullThe bank or wallet the message is from. Null for an unsupported sender.
keystringnamestring
typestring, or nullThe kind of message, e.g. TRANSFER_TB_FROM_TB. A label, not a fixed list. Null unless parsed.
direction"incoming" or "outgoing", or nullNull when the message moves no money in or out of the account: a reminder, a failed transfer, a package notice.
amountmoney, or nullfeemoney, or nullbalanceAftermoney, or nullThe balance the message states, if any.
participantslist of objectThe people, merchants and accounts the message names.
role"sender" or "receiver"namestring, or nullidentifierstring, or nullPhone or account number as the SMS writes it, often masked.
typestringThe kind of account: TELEBIRR, CBE, MERCHANT, UNKNOWN.
referencestring, or nullThe provider's transaction reference.
receiptUrlstring, or nullA link to the receipt, when the message has one.
Webhooks
Add up to 5 HTTPS addresses under Webhooks on your account page, and Odit POSTs each new or changed transaction to them, about 30 seconds after it lands. A webhook never carries the SMS text.
Requests are signed per Standard Webhooks, with the whsec_ secret you got when you added the address. Check the signature before you trust a body:
import { Webhook } from 'standardwebhooks';
const wh = new Webhook(process.env.ODIT_WEBHOOK_SECRET); // whsec_...
// The body exactly as it arrived, before any JSON parsing.
const event = wh.verify(rawBody, {
'webhook-id': req.headers['webhook-id'],
'webhook-timestamp': req.headers['webhook-timestamp'],
'webhook-signature': req.headers['webhook-signature'],
});Answer with any 2xx. Anything else is tried again after 5 seconds, 5 minutes, 30 minutes, 2, 5, 10 and 10 hours, then dropped. The webhook-id stays the same on every try, so you can skip one you already handled. An address that has failed for 3 days is turned off and you get an email.
transaction.created
A new transaction, about 30 seconds after it lands. Never carries rawSms. Not sent for the SMS a phone already had when it first synced, or when your account joined the API.
Body
{
"type": "transaction.created",
"timestamp": "2026-10-12T08:41:36.000Z",
"data": {
"id": 48210,
"device": "00000000-0000-0000-0000-000000000001",
"occurredAt": "2026-10-12T08:41:00.000Z",
"receivedAt": "2026-10-12T08:41:06.000Z",
"direction": "outgoing",
"type": "TRANSFER_CBE_TO_TB",
"amount": { "amount": "1250.00", "currency": "ETB" },
"fee": { "amount": "5.00", "currency": "ETB" },
"balanceAfter": { "amount": "18420.55", "currency": "ETB" },
"provider": { "key": "cbe", "name": "CBE" },
"account": { "id": 312, "name": "CBE (Salary)" },
"counterparty": {
"name": "Abebe Kebede",
"identifier": "251911000000",
"type": "TELEBIRR"
},
"category": null,
"isInternalTransfer": false,
"isDebt": false,
"note": null,
"reference": "FT0000000000",
"source": "sms",
"updatedAt": "2026-10-12T08:41:06.000Z"
}
}data is a Transaction, the same shape GET /v1/transactions answers with.
transaction.updated
A transaction changed in a way the API shows. Renaming a category, account or person is not a change to its transactions.
Body
{
"type": "transaction.updated",
"timestamp": "2026-10-12T09:03:48.000Z",
"data": {
"id": 48210,
"device": "00000000-0000-0000-0000-000000000001",
"occurredAt": "2026-10-12T08:41:00.000Z",
"receivedAt": "2026-10-12T08:41:06.000Z",
"direction": "outgoing",
"type": "TRANSFER_CBE_TO_TB",
"amount": { "amount": "1250.00", "currency": "ETB" },
"fee": { "amount": "5.00", "currency": "ETB" },
"balanceAfter": { "amount": "18420.55", "currency": "ETB" },
"provider": { "key": "cbe", "name": "CBE" },
"account": { "id": 312, "name": "CBE (Salary)" },
"counterparty": {
"name": "Abebe Kebede",
"identifier": "251911000000",
"type": "TELEBIRR"
},
"category": { "id": 7, "name": "Family" },
"isInternalTransfer": false,
"isDebt": false,
"note": "For the house",
"reference": "FT0000000000",
"source": "sms",
"updatedAt": "2026-10-12T09:03:18.000Z"
}
}data is a Transaction, the same shape GET /v1/transactions answers with.
transaction.deleted
A transaction is gone, or Odit no longer reads it as one.
Body
{
"type": "transaction.deleted",
"timestamp": "2026-10-13T11:20:05.000Z",
"data": {
"id": 48210,
"device": "00000000-0000-0000-0000-000000000001"
}
}data has these fields:
idintegerdevicestring, or null
transactions.backfilled
More than 200 transactions arrived at once (an old phone, an import). Fetch them from /v1/transactions instead of one event each.
Body
{
"type": "transactions.backfilled",
"timestamp": "2026-10-12T10:02:41.000Z",
"data": {
"device": "00000000-0000-0000-0000-000000000001",
"count": 1840,
"from": "2025-04-02T05:10:00.000Z",
"to": "2026-10-12T08:41:00.000Z"
}
}data has these fields:
devicestringcountintegerfromstringThe oldest SMS time among them. ISO 8601, UTC.
tostringThe newest SMS time among them. ISO 8601, UTC.
device.merged
Two of your phones were merged; from's transactions now belong to into. No per-transaction events follow.
Body
{
"type": "device.merged",
"timestamp": "2026-10-14T16:45:12.000Z",
"data": {
"from": "00000000-0000-0000-0000-000000000002",
"into": "00000000-0000-0000-0000-000000000001"
}
}data has these fields:
fromstringintostring, or null
device.deleted
A phone was removed from your account. No per-transaction events follow.
Body
{
"type": "device.deleted",
"timestamp": "2026-10-15T07:30:00.000Z",
"data": { "device": "00000000-0000-0000-0000-000000000002" }
}data has these fields:
devicestring
webhook.test
Sent when you press "Send test event" on the account page.
Body
{
"type": "webhook.test",
"timestamp": "2026-10-12T09:10:00.000Z",
"data": { "message": "A test from Odit. Your webhook works." }
}data has these fields:
messagestring
Senders
sender is the name or number your phone shows the SMS as, in any case. To read a message as one provider whatever its sender, send that provider's key as provider instead.
Wallets
- Telebirrtelebirr
127Telebirr+251127Unknown127 - M-PESAmpesa
MPESA - CBE Birrcbebirr
CBE BirrCBE-BirrCBEBirr - Apolloapollo
apollo - COOPay Ebirrcoopay
COOPayEBIRRCOOPay_E-BiCooPay_Ebir836 - Kaafi Ebirrkaafi
EBIRR602 - HalalPayhalalpay
HalalPay
Banks
- CBEcbe
CBE889 - BOAboa
BOA - Zemen Bankzemenbank
Zemen BankZemenBank - Awash Bankawashbank
Awash BankAwashBank - Dashen Bankdashenbank
Dashen BankDashenBank - Enat Bankenatbank
ENAT BANKENATBANK - Amhara Bankamharabank
AMHARA BANKAMHARABANK - Nib Banknibbank
NIB - Berhan Bankberhanbank
Berhan BankBerhanBank - Hibret Bankhibretbank
HibretBankUnited Bank8818 - Ahadu Bankahadubank
Ahadu BankAhaduBank - Hijra Bankhijrabank
HIJRA BANKHIJRABANK - Coopbankcoopbank
CBO - Oromia Bankoromiabank
Oromia BankOromiaBankOIB - Abay Bankabaybank
ABAY BANKABAYBANK - Wegagen Bankwegagenbank
WegagenBank - ZamZam Bankzamzambank
ZamZam BankZamZamBank - Tsehay Banktsehaybank
Tsehay BankTsehayBank - Bunna Bankbunnabank
Bunna BankBunnaBank - Siinqee Banksiinqeebank
SiinqeeBank - Anbesa Bankanbesabank
AnbesaBankLIB - Tsedey Banktsedeybank
Tsedey Bank - Global Bankglobalbank
GBEDGB - Gadaa Bankgadaabank
Gadaa Bank - Addis Bankaddisbank
Addis Bank - Siket Banksiketbank
Siket Bank - Goh Betoch Bankgohbetochbank
GOH BANK - Rammis Bankrammisbank
RAMMIS BANK - Sidama Banksidamabank
Sidama BankSidama - Omo Bankomobank
Omo Bank - Shabelle Bankshabellebank
Shabelle Ba
Telecoms
- Ethio telecomethiotelecom
994251994+251994+251251994 - Safaricomsafaricom
Safaricom