Developers

The API, in plain words

Everything your team does in the CRM, your own code and AI assistants can do too: one REST API, an MCP server for assistants, and webhooks when things happen.

Your first call, in three steps

You need an account with the API on its plan, and to be an owner or admin. It takes about two minutes.

  1. Make a keyIn the app, open Developers and choose New API key. Give it a name you will recognise, like Website or Claude, and tick only what it needs. Copy the key: it starts adaab_ and is shown once.
  2. Check it worksAsk who the key belongs to. The answer names your organisation and lists every operation this key may call.
  3. Do something realPick an operation below. Each one has its fields, what comes back, the errors to expect, and the same call in curl, JavaScript, Python and as an MCP tool call, ready to copy.
curl https://arm.adab.solutions/api/v1/account \
  -H "Authorization: Bearer $ADAAB_API_KEY"

Keep keys out of your code: put them in an environment variable such as ADAAB_API_KEY, as the examples do.

Keys and sign-in

An owner or admin makes a key under Developers in the app, and chooses what it may do. Send it on every request as a bearer token. A key can only call what its organisation’s plan includes and what its permissions allow; the rest answer 403.

A key acts as the person who made it: notes and tasks it writes show their name, and it never has more permissions than they do. If they leave the team or lose a permission, the key loses it too. Revoke a key under Developers and anything using it stops at once.

Authorization: Bearer adaab_…

Every address below starts with https://arm.adab.solutions/api/v1. The same API as an OpenAPI document: /api/v1/openapi.json

Lists, dates and ids

IdsEvery record has an id of 12 letters and digits, like k3Jd9QmZp2Lx. They never change.
ListsLists come back as { "data": [ … ] }. Most take limit (up to 200, 50 by default) and offset. To get everything, add limit to offset until fewer than limit come back.
Dates and timesSend days like 2026-10-01. Times come back in UTC, like 2026-10-01T09:30:00.000Z.
MoneyAmounts are in pounds (or the currency given), not pennies: send 25.50 for £25.50. Currencies are three letters and default to GBP.
Safe retriesRecording a donation takes your own external_id, so sending the same gift twice records it once. Adding a contact with an email or phone that already exists updates them instead of making a duplicate.
Sending dataGET requests take fields in the address (?q=amina). POST and PATCH take a JSON body with Content-Type: application/json.

Errors and limits

Anything that goes wrong comes back with an HTTP status and a sentence saying what happened. Wrong input also lists each problem with the field it is about:

{
  "error": "Invalid input",
  "issues": [
    {
      "path": "email",
      "message": "Invalid email address"
    }
  ]
}
CodeWhat it meansWhat to do
400The input was wrong. The answer lists each problem with the field it is about.Fix the fields named in issues and send it again.
401No key, or a key that was revoked.Check the Authorization header, or make a new key.
402The organisation is paused: its trial ended or payment failed. It works again once someone chooses a plan.An owner chooses a plan under Plan and billing.
403The plan does not include this, or the key lacks the permission.Call /account to see what this key may do; make a key with the permission it needs.
404Nothing with that id in this organisation.Check the id, or search for the record first.
429More than 600 requests a minute from one key. Wait a moment and try again.Slow down and retry after the time in the RateLimit headers.
500Something went wrong on our side.Try again in a minute. If it keeps happening, tell us the time and the address you called.

Lists take limit (up to 200, 50 by default) and offset. Ids are 12 letters and digits. Dates are like 2026-10-01; times come back in UTC.

AI assistants (MCP)

Claude and other assistants that speak the Model Context Protocol connect to the address below with the same key. Each operation on this page becomes a tool, and an assistant only sees the tools its key may use.

https://arm.adab.solutions/mcp

Each tool carries the same descriptions as this page, so the assistant knows what every field means. Tools that only read are marked read-only, so a careful assistant can use them without asking you first.

claude mcp add --transport http adaab https://arm.adab.solutions/mcp \
  --header "Authorization: Bearer adaab_…"

Signed in? The step-by-step guide in the app, under Developers, fills in your address and checks the connection for you. Connect an assistant

Webhooks

Add an https address under Developers and choose the events. Each one arrives as a JSON POST with an X-Adaab-Signature header, t=<time>,v1=<signature>: the signature is an HMAC-SHA256, with your endpoint’s secret, of the time, a dot and the body. A failed delivery is tried again after 1, 2, 4, 8 and 16 minutes.

Check the signature before trusting a webhook, and refuse ones whose time is more than five minutes old:

import crypto from "node:crypto";

function verify(header, rawBody, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map(p => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const want = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(want), Buffer.from(v1));
}
  • contact_created
  • status_changed
  • tag_added
  • list_joined
  • message_sent
  • donation_received

Supporters (contacts and leads)

GET /contacts/:id

One contact with their notes, open tasks, lists and giving summary.

Everything about one person in one call: their details, the last 20 notes, the lists they are on and a giving summary per currency. Use it before writing to someone or when an assistant needs context about a supporter.

Permission contacts:read · MCP tool contacts_get

FieldTypeNotes
id requiredpath string The contact's id, 12 letters and digits.Example "k3Jd9QmZp2Lx"

What comes back: The contact, plus notes (newest first), lists ({ id, name }) and giving ({ currency, gifts, total, last_gift }).

  • 404 No contact with that id in this organisation. Search for them with contacts_search.
curl "https://arm.adab.solutions/api/v1/contacts/k3Jd9QmZp2Lx" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "id": "k3Jd9QmZp2Lx",
  "first_name": "Amina",
  "last_name": "Khan",
  "email": "amina@example.org",
  "phone": "+447700900123",
  "status": "donor",
  "source": "website",
  "campaign": "Ramadan 2026",
  "tags": [
    "monthly",
    "volunteer"
  ],
  "fields": {
    "city": "Leeds"
  },
  "email_opt_out": false,
  "whatsapp_opt_out": false,
  "created_at": "2026-09-14T10:22:05.000Z",
  "updated_at": "2026-10-01T08:00:00.000Z",
  "notes": [
    {
      "id": "Nte1Aa2Bb3Cc",
      "body": "Prefers WhatsApp.",
      "created_at": "2026-09-20T12:00:00.000Z"
    }
  ],
  "lists": [
    {
      "id": "Lst7Hq2Wn4Ra",
      "name": "Monthly givers"
    }
  ],
  "giving": [
    {
      "currency": "GBP",
      "gifts": 7,
      "total": 350,
      "last_gift": "2026-09-30"
    }
  ]
}

POST /contacts

Create a contact, or update the existing one with the same email or phone (upsert). Fires automations and webhooks.

Adds a person, or updates them if someone with the same email or phone is already there, so it is safe to call from a sign-up form every time. Only the fields you send are set. New people start any "new contact" automations and send the contact.created webhook; updates send contact.updated.

Permission contacts:write · MCP tool contacts_create

FieldTypeNotes
first_namebody string Their first name. Up to 100 characters.Example "Amina"
last_namebody string Their last name. Up to 100 characters.Example "Khan"
emailbody email | null Their email address. Used to find an existing person.Example "amina@example.org"
phonebody string | null Their phone number, best with the country code (+44…). Used to find an existing person.
statusbody string Where they are with you, e.g. lead, donor, volunteer. Up to 80 characters.
sourcebody string Where they came from, e.g. website or event. Up to 200 characters.Example "website"
campaignbody string The campaign that brought them in. Up to 200 characters.
tagsbody string[] Labels to add. Up to 50. Up to 50.Example ["newsletter"]
fieldsbody object Custom columns by key, e.g. {"city": "Leeds"}. See Columns in the app for the keys.
email_opt_outbody boolean true if they asked not to be emailed.
whatsapp_opt_outbody boolean true if they asked not to get WhatsApp messages.
sms_opt_outbody boolean true if they asked not to get text messages.

What comes back: { created, contact }. created is true for a new person, false when an existing one was updated.

  • 400 Send at least an email, a phone or a first name.
curl -X POST "https://arm.adab.solutions/api/v1/contacts" \
  -H "Authorization: Bearer $ADAAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"first_name":"Amina","last_name":"Khan","email":"amina@example.org","source":"website","tags":["newsletter"]}'
Example response
{
  "created": true,
  "contact": {
    "id": "k3Jd9QmZp2Lx",
    "first_name": "Amina",
    "last_name": "Khan",
    "email": "amina@example.org",
    "phone": "+447700900123",
    "status": "donor",
    "source": "website",
    "campaign": "Ramadan 2026",
    "tags": [
      "monthly",
      "volunteer"
    ],
    "fields": {
      "city": "Leeds"
    },
    "email_opt_out": false,
    "whatsapp_opt_out": false,
    "created_at": "2026-09-14T10:22:05.000Z",
    "updated_at": "2026-10-01T08:00:00.000Z"
  }
}

PATCH /contacts/:id

Change a contact. Only the fields given change; custom fields are merged.

Changes some of a person's details. Fields you leave out stay as they are, and custom fields are merged, so sending {"fields": {"city": "York"}} keeps their other custom fields.

Permission contacts:write · MCP tool contacts_update

FieldTypeNotes
id requiredpath string The contact's id.Example "k3Jd9QmZp2Lx"
first_namebody string Up to 100 characters.
last_namebody string Up to 100 characters.
emailbody email | null
phonebody string | null
statusbody string Up to 80 characters.Example "donor"
sourcebody string Up to 200 characters.
campaignbody string Up to 200 characters.
tagsbody string[] Up to 50.
fieldsbody object Custom columns by key, e.g. {"city": "Leeds"}Example {"city":"York"}
email_opt_outbody boolean
whatsapp_opt_outbody boolean
sms_opt_outbody boolean

What comes back: The contact as it is now.

  • 404 No contact with that id in this organisation.
curl -X PATCH "https://arm.adab.solutions/api/v1/contacts/k3Jd9QmZp2Lx" \
  -H "Authorization: Bearer $ADAAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"donor","fields":{"city":"York"}}'
Example response
{
  "id": "k3Jd9QmZp2Lx",
  "first_name": "Amina",
  "last_name": "Khan",
  "email": "amina@example.org",
  "phone": "+447700900123",
  "status": "donor",
  "source": "website",
  "campaign": "Ramadan 2026",
  "tags": [
    "monthly",
    "volunteer"
  ],
  "fields": {
    "city": "York"
  },
  "email_opt_out": false,
  "whatsapp_opt_out": false,
  "created_at": "2026-09-14T10:22:05.000Z",
  "updated_at": "2026-10-01T08:00:00.000Z"
}

POST /contacts/:id/notes

Add a note to a contact.

Writes a note on someone's record, as the person who made the API key. Good for logging a conversation, a phone call or what an assistant found out.

Permission contacts:write · MCP tool notes_add

FieldTypeNotes
id requiredpath string The contact's id.Example "k3Jd9QmZp2Lx"
body requiredbody string The note, up to 5,000 characters. Up to 5000 characters.Example "Called to say thank you. Happy to hear about the winter appeal."

What comes back: The note: { id, body, created_at }.

  • 404 No contact with that id.
curl -X POST "https://arm.adab.solutions/api/v1/contacts/k3Jd9QmZp2Lx/notes" \
  -H "Authorization: Bearer $ADAAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"body":"Called to say thank you. Happy to hear about the winter appeal."}'
Example response
{
  "id": "Nte9Zz8Yy7Xx",
  "body": "Called to say thank you. Happy to hear about the winter appeal.",
  "created_at": "2026-10-04T09:30:00.000Z"
}

Customer lists

GET /lists

Every list, with how many people are on it.

Every list in the organisation with how many people are on it. Use the ids with contacts_search, lists_add_contacts and letters_queue.

Permission lists:read · MCP tool lists_list

No fields: just call it.

What comes back: { data: [{ id, name, description, members }, …] }, by name.

curl "https://arm.adab.solutions/api/v1/lists" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "data": [
    {
      "id": "Lst7Hq2Wn4Ra",
      "name": "Monthly givers",
      "description": "Anyone with an active standing order",
      "members": 214
    }
  ]
}

POST /lists/:id/members

Add contacts to a list (fires "joined list" automations).

Puts people on a list. People already on it are left alone and not counted. Each newly added person starts the list's "joined list" automations.

Permission lists:write · MCP tool lists_add_contacts

FieldTypeNotes
id requiredpath string The list's id.Example "Lst7Hq2Wn4Ra"
contact_ids requiredbody string[] The people to add, 1 to 500 ids. Up to 500.Example ["k3Jd9QmZp2Lx"]

What comes back: { added }: how many were newly added.

  • 404 No list with that id in this organisation.
curl -X POST "https://arm.adab.solutions/api/v1/lists/Lst7Hq2Wn4Ra/members" \
  -H "Authorization: Bearer $ADAAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contact_ids":["k3Jd9QmZp2Lx"]}'
Example response
{
  "added": 1
}

Tasks and reminders

GET /tasks

Tasks that are not done yet (or done ones), optionally for one contact.

Things the team has to do. By default only open tasks, soonest due first; send done=true for finished ones.

Permission tasks:read · MCP tool tasks_list

FieldTypeNotes
contact_idquery string Only tasks about this person.Example "k3Jd9QmZp2Lx"
donequery boolean true for finished tasks, false (the default) for open ones. Default false.
limitquery integer From 1 to 200. Default 50.
offsetquery integer From 0 to 9007199254740991. Default 0.

What comes back: { data: [{ id, title, due_on, contact_id, assignee_id, done_at, created_at }, …] }.

curl "https://arm.adab.solutions/api/v1/tasks?contact_id=k3Jd9QmZp2Lx" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "data": [
    {
      "id": "Tsk5Bv8Cx1Ny",
      "title": "Call Amina about Gift Aid",
      "due_on": "2026-10-07",
      "contact_id": "k3Jd9QmZp2Lx",
      "assignee_id": "Usr4Ee5Rr6Tt",
      "done_at": null,
      "created_at": "2026-10-01T09:00:00.000Z"
    }
  ]
}

POST /tasks

Create a task, optionally about a contact and for a member (defaults to the key's owner).

Adds a task, optionally about a person and for a team member. Without assignee_id it goes to whoever made the API key.

Permission tasks:write · MCP tool tasks_create

FieldTypeNotes
title requiredbody string What needs doing, up to 200 characters. Up to 200 characters.Example "Call Amina about Gift Aid"
due_onbody string The day it is due, like 2026-10-07.Example "2026-10-07"
contact_idbody string The person it is about.Example "k3Jd9QmZp2Lx"
assignee_idbody string The team member who should do it.

What comes back: { id, title, due_on }.

  • 404 The contact was not found.
  • 400 assignee_id is not a member of this organisation.
curl -X POST "https://arm.adab.solutions/api/v1/tasks" \
  -H "Authorization: Bearer $ADAAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Call Amina about Gift Aid","due_on":"2026-10-07","contact_id":"k3Jd9QmZp2Lx"}'
Example response
{
  "id": "Tsk5Bv8Cx1Ny",
  "title": "Call Amina about Gift Aid",
  "due_on": "2026-10-07"
}

POST /tasks/:id/done

Mark a task done.

Ticks a task off. Doing it twice is harmless: the first finish time is kept.

Permission tasks:write · MCP tool tasks_complete

FieldTypeNotes
id requiredpath string The task's id.Example "Tsk5Bv8Cx1Ny"

What comes back: { id, done_at }.

  • 404 No task with that id.
curl -X POST "https://arm.adab.solutions/api/v1/tasks/Tsk5Bv8Cx1Ny/done" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "id": "Tsk5Bv8Cx1Ny",
  "done_at": "2026-10-04T11:15:00.000Z"
}

Gifts (donations, funds and Gift Aid claims)

GET /donations

Donations, newest first, filtered by contact, fund or date range.

Gifts received, newest first. Refunded gifts are included with refunded_on set, so you can match your own records exactly.

Permission donations:read · MCP tool donations_list

FieldTypeNotes
contact_idquery string Only this person's gifts.Example "k3Jd9QmZp2Lx"
fundquery string Only gifts to this fund, e.g. General or Zakat.
fromquery string Received on or after this day.Example "2026-01-01"
toquery string Received on or before this day.
limitquery integer From 1 to 200. Default 50.
offsetquery integer From 0 to 9007199254740991. Default 0.

What comes back: { data: [{ id, contact_id, amount, currency, received_on, fund, method, reference, gift_aid, claim_id, pledge_id, refunded_on, created_at }, …] }.

curl "https://arm.adab.solutions/api/v1/donations?contact_id=k3Jd9QmZp2Lx&from=2026-01-01" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "data": [
    {
      "id": "Don2Rt6Yp9Ke",
      "contact_id": "k3Jd9QmZp2Lx",
      "amount": "50.00",
      "currency": "GBP",
      "received_on": "2026-09-30",
      "fund": "General",
      "method": "card",
      "reference": "WEB-1042",
      "gift_aid": true,
      "claim_id": null,
      "pledge_id": null,
      "refunded_on": null,
      "created_at": "2026-09-30T18:02:11.000Z"
    }
  ]
}

POST /donations

Record a donation for a contact. Give external_id (your system's id) so a retry never records it twice. Fires thank-you automations.

Records a gift from a person. Send external_id with your own system's id for the gift: if the same external_id comes again (a retry, a re-sync) nothing is recorded twice. Recording a gift starts thank-you automations and sends the donation.created webhook. Tick gift_aid only when they have a valid declaration.

Permission donations:write · MCP tool donations_record

FieldTypeNotes
contact_id requiredbody string Who gave.Example "k3Jd9QmZp2Lx"
amount requiredbody number How much, e.g. 25 or 25.50. At most 10000000.Example 25
currencybody string Three-letter currency, GBP by default. Default "GBP".
received_onbody string The day it arrived; today if left out.
fundbody string Which fund it goes to. General by default. Up to 80 characters. Default "General".Example "Winter appeal"
methodbody string How it was paid, e.g. card, cash, bank transfer. Up to 60 characters. Default "".Example "card"
referencebody string Your reference for it, shown on their record. Up to 120 characters. Default "".
gift_aidbody boolean true if Gift Aid can be claimed on it. Default false.Example true
pledge_idbody string A pledge of theirs this pays towards.
external_idbody string Your system's id for the gift, so it is never recorded twice. Up to 200 characters.Example "stripe_pi_3Px"

What comes back: { recorded: true, donation } or, when the external_id was seen before, { recorded: false, reason }.

  • 404 The contact was not found.
  • 400 The pledge belongs to someone else.
curl -X POST "https://arm.adab.solutions/api/v1/donations" \
  -H "Authorization: Bearer $ADAAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contact_id":"k3Jd9QmZp2Lx","amount":25,"fund":"Winter appeal","method":"card","gift_aid":true,"external_id":"stripe_pi_3Px"}'
Example response
{
  "recorded": true,
  "donation": {
    "id": "Don2Rt6Yp9Ke",
    "amount": "25.00",
    "currency": "GBP",
    "received_on": "2026-10-04",
    "fund": "Winter appeal"
  }
}

POST /donations/:id/refund

Mark a donation refunded. It stays on record but stops counting and is never claimed for Gift Aid.

Marks a gift as refunded. It stays on their record for history but no longer counts in totals and is never claimed for Gift Aid. If it was already in a Gift Aid claim, the answer tells you which claim needs adjusting.

Permission donations:write · MCP tool donations_refund

FieldTypeNotes
id requiredpath string The donation's id.Example "Don2Rt6Yp9Ke"
reasonbody string Why, for the record. Up to 500 characters. Default "".Example "Paid twice by mistake"
refunded_onbody string The day of the refund; today if left out.

What comes back: { refunded: true, gift_aid_claim_to_adjust }: the claim id, or null.

  • 404 No donation with that id.
curl -X POST "https://arm.adab.solutions/api/v1/donations/Don2Rt6Yp9Ke/refund" \
  -H "Authorization: Bearer $ADAAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Paid twice by mistake"}'
Example response
{
  "refunded": true,
  "gift_aid_claim_to_adjust": null
}

GET /donations/summary

Income by month and fund over a date range (default: the last 12 months).

Income totals by month, fund and currency, leaving out refunds. Handy for a dashboard or for an assistant answering "how did the appeal do?".

Permission donations:read · MCP tool donations_summary

FieldTypeNotes
fromquery string First day to count; 12 months ago if left out.Example "2026-09-01"
toquery string Last day to count; today if left out.Example "2026-09-30"

What comes back: { data: [{ month, fund, currency, gifts, total }, …] } in month order.

curl "https://arm.adab.solutions/api/v1/donations/summary?from=2026-09-01&to=2026-09-30" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "data": [
    {
      "month": "2026-09",
      "fund": "General",
      "currency": "GBP",
      "gifts": 42,
      "total": 3150.5
    }
  ]
}

GET /recurring-gifts

Active recurring gifts (standing orders, Direct Debits, card subscriptions).

Regular gifts that are still running: standing orders, Direct Debits and card subscriptions.

Permission donations:read · MCP tool recurring_gifts_list

FieldTypeNotes
contact_idquery string Only this person's.Example "k3Jd9QmZp2Lx"

What comes back: { data: [{ id, contact_id, amount, currency, frequency, fund, method, gift_aid, started_on }, …] }.

curl "https://arm.adab.solutions/api/v1/recurring-gifts?contact_id=k3Jd9QmZp2Lx" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "data": [
    {
      "id": "Rec3Qq4Ww5Ee",
      "contact_id": "k3Jd9QmZp2Lx",
      "amount": "10.00",
      "currency": "GBP",
      "frequency": "monthly",
      "fund": "General",
      "method": "direct_debit",
      "gift_aid": true,
      "started_on": "2025-01-01"
    }
  ]
}

GET /donors/rfm

Donors scored 1-5 on recency, frequency and money given, with a segment: Champions, Loyal, Big givers, New, At risk, Lapsed, Needs attention.

Scores every donor from 1 to 5 on how recently, how often and how much they give, and names a segment. Use it to choose who to thank, upgrade or win back.

Permission donations:read · MCP tool donors_rfm

FieldTypeNotes
segmentquery "Champions" | "Loyal" | "Big givers" | "New" | "At risk" | "Lapsed" | "Needs attention" Only donors in this segment.Example "At risk"
contact_idquery string Only this donor.
currencyquery string Which currency to score in, GBP by default. Default "GBP".
limitquery integer How many, 1 to 2,000. From 1 to 2000. Default 200.Example 50

What comes back: { data: [{ contact_id, first_name, last_name, email, last_gift, gifts, total, r, f, m, segment }, …] }. r, f and m are the 1 to 5 scores; up to 500 donors.

curl "https://arm.adab.solutions/api/v1/donors/rfm?segment=At+risk&limit=50" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "data": [
    {
      "contact_id": "k3Jd9QmZp2Lx",
      "first_name": "Amina",
      "last_name": "Khan",
      "email": "amina@example.org",
      "last_gift": "2025-11-02",
      "gifts": 9,
      "total": 640,
      "r": 2,
      "f": 4,
      "m": 4,
      "segment": "At risk"
    }
  ]
}

Pledges: promises to give, tracked against donations

GET /pledges

Pledges with how much has been paid and their status (open, overdue, fulfilled, cancelled).

Promises to give, with how much has come in against each.

Permission donations:read · MCP tool pledges_list

FieldTypeNotes
contact_idquery string Only this person's pledges.
statusquery "open" | "overdue" | "fulfilled" | "cancelled" open, overdue, fulfilled or cancelled.Example "overdue"

What comes back: { data: [{ id, contact_id, amount, currency, fund, due_on, notes, cancelled_on, created_at, paid, status }, …] }: open and overdue first, up to 500.

curl "https://arm.adab.solutions/api/v1/pledges?status=overdue" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "data": [
    {
      "id": "Plg4Mn7Qs2Vb",
      "contact_id": "k3Jd9QmZp2Lx",
      "amount": "500.00",
      "currency": "GBP",
      "fund": "Building",
      "due_on": "2026-09-01",
      "notes": "",
      "cancelled_on": null,
      "created_at": "2026-06-01T10:00:00.000Z",
      "paid": 200,
      "status": "overdue"
    }
  ]
}

POST /pledges

Record a promise to give. Donations recorded with its pledge_id count towards it.

Records a promise to give. Gifts recorded later with this pledge_id count towards it, and it becomes fulfilled when they add up.

Permission donations:write · MCP tool pledges_create

FieldTypeNotes
contact_id requiredbody string Who promised.Example "k3Jd9QmZp2Lx"
amount requiredbody number How much they promised. At most 10000000.Example 500
currencybody string Three-letter currency, GBP by default. Default "GBP".
fundbody string Which fund. General by default. Up to 80 characters. Default "General".Example "Building"
due_onbody string When it should be paid by.Example "2026-12-31"
notesbody string Anything worth remembering. Up to 2000 characters. Default "".

What comes back: { id }: the new pledge's id. Send it as pledge_id when recording the gifts that pay it.

curl -X POST "https://arm.adab.solutions/api/v1/pledges" \
  -H "Authorization: Bearer $ADAAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contact_id":"k3Jd9QmZp2Lx","amount":500,"fund":"Building","due_on":"2026-12-31"}'
Example response
{
  "id": "Plg4Mn7Qs2Vb"
}

Sponsorship schemes (orphans, students...)

GET /beneficiaries

Sponsorship beneficiaries and their current sponsor, if any.

People or projects that can be sponsored, with their sponsor if they have one.

Permission sponsorships:read · MCP tool beneficiaries_list

FieldTypeNotes
schemequery string Only this sponsorship scheme.
unsponsoredquery boolean true to see only those still waiting for a sponsor. Default false.Example true

What comes back: { data: [{ id, code, name, scheme, country, sponsor_id, amount, currency, started_on }, …] }.

curl "https://arm.adab.solutions/api/v1/beneficiaries?unsponsored=true" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "data": [
    {
      "id": "Ben5Tt6Yy7Uu",
      "code": "ORP-0142",
      "name": "Yusuf",
      "scheme": "Orphan care",
      "country": "Kenya",
      "sponsor_id": null,
      "amount": null,
      "currency": null,
      "started_on": null
    }
  ]
}

Call centre: calling campaigns with outcomes and targets

GET /call-campaigns

Calling campaigns with calls made, money raised and pledged against target.

Phone campaigns with progress: calls made, money raised and pledged, against the target.

Permission calls:make · MCP tool call_campaigns_list

No fields: just call it.

What comes back: { data: [{ id, name, list_id, target, active, created_at, calls, people, raised, pledged, outcomes }, …] }.

curl "https://arm.adab.solutions/api/v1/call-campaigns" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "data": [
    {
      "id": "Cmp8Fz3Gh6Jw",
      "name": "Winter thank-you calls",
      "list_id": "Lst7Hq2Wn4Ra",
      "target": 5000,
      "active": true,
      "created_at": "2026-09-01T09:00:00.000Z",
      "calls": 38,
      "people": 35,
      "raised": 1240,
      "pledged": 600,
      "outcomes": [
        {
          "outcome": "donated",
          "n": 12
        }
      ]
    }
  ]
}

POST /call-campaigns/:id/next

Take the next person to call in a campaign (or your call still open). Returns the call id and the contact.

Gives you the next person to ring in a campaign, and holds them for you so nobody else calls them at the same time. If you already have a call open, you get that one back.

Permission calls:make · MCP tool calls_next

FieldTypeNotes
id requiredpath string The campaign's id.Example "Cmp8Fz3Gh6Jw"

What comes back: { call_id, contact } or, when nobody is left, { call: null, message }.

curl -X POST "https://arm.adab.solutions/api/v1/call-campaigns/Cmp8Fz3Gh6Jw/next" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "call_id": "Cal1Xs5Dk8Tq",
  "contact": {
    "id": "k3Jd9QmZp2Lx",
    "first_name": "Amina",
    "last_name": "Khan",
    "phone": "+447700900123",
    "email": "amina@example.org"
  }
}

POST /calls/:id

Close your open call with its outcome. donated records a donation, pledged a pledge, callback a task.

Closes your open call with what happened. donated also records the gift (send amount), pledged records a pledge, and callback makes a task for callback_on.

Permission calls:make · MCP tool calls_log

FieldTypeNotes
id requiredpath string The call id from calls_next.Example "Cal1Xs5Dk8Tq"
outcome requiredbody "answered" | "donated" | "pledged" | "callback" | "no_answer" | "voicemail" | "declined" | "wrong_number" | "do_not_call" What happened on the call.Example "donated"
notesbody string Notes from the call. Up to 5000 characters. Default "".Example "Gave over the phone."
amountbody number For donated or pledged: how much. At most 10000000.Example 30
fundbody string The fund for the gift or pledge. Up to 80 characters. Default "General".
callback_onbody string For callback: the day to ring again.

What comes back: { logged: true }.

curl -X POST "https://arm.adab.solutions/api/v1/calls/Cal1Xs5Dk8Tq" \
  -H "Authorization: Bearer $ADAAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"outcome":"donated","amount":30,"notes":"Gave over the phone."}'
Example response
{
  "logged": true
}

Letters: merge-field letters and a print queue

POST /letters

Queue a letter template for contacts (or everyone on a list) to be printed. Contacts missing a merge field are skipped.

Puts a letter template in the print queue for some people, or everyone on a list. People missing something the letter needs (like an address) are skipped and counted.

Permission letters:write · MCP tool letters_queue

FieldTypeNotes
template_id requiredbody string The letter template.Example "Tpl6Wq9Er3Zu"
contact_idsbody string[] The people to write to. Up to 5000. Default [].
list_idbody string Or everyone on this list.Example "Lst7Hq2Wn4Ra"

What comes back: { queued, skipped, already }: letters queued, people skipped for missing details, and people who already had this letter waiting.

  • 400 Send contact_ids or a list_id.
curl -X POST "https://arm.adab.solutions/api/v1/letters" \
  -H "Authorization: Bearer $ADAAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"template_id":"Tpl6Wq9Er3Zu","list_id":"Lst7Hq2Wn4Ra"}'
Example response
{
  "queued": 212,
  "skipped": 2,
  "already": 0
}

Email + WhatsApp templates and builder

GET /templates

Email and WhatsApp templates that can be sent.

The email and WhatsApp templates you can send with messages_send.

Permission templates:read · MCP tool templates_list

No fields: just call it.

What comes back: { data: [{ id, channel, name, subject, active }, …] }.

curl "https://arm.adab.solutions/api/v1/templates" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "data": [
    {
      "id": "Tpl6Wq9Er3Zu",
      "channel": "email",
      "name": "Thank you for your gift",
      "subject": "Thank you, {{first_name}}",
      "active": true
    }
  ]
}

POST /messages

Send an email or WhatsApp template to a contact through the outbox (opt-outs, caps and pauses apply).

Sends one template to one person through the outbox, exactly as the team would. Opt-outs, daily caps and paused channels are respected, so a message may be held or blocked rather than sent at once; check its status with messages_list.

Permission messages:send · MCP tool messages_send

FieldTypeNotes
contact_id requiredbody string Who to send to.Example "k3Jd9QmZp2Lx"
template_id requiredbody string An active template (see templates_list).Example "Tpl6Wq9Er3Zu"

What comes back: { queued, message_id }. queued is false when nothing could be sent, for example because they opted out.

  • 400 The template is switched off or does not exist.
  • 404 The contact was not found.
curl -X POST "https://arm.adab.solutions/api/v1/messages" \
  -H "Authorization: Bearer $ADAAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contact_id":"k3Jd9QmZp2Lx","template_id":"Tpl6Wq9Er3Zu"}'
Example response
{
  "queued": true,
  "message_id": "Msg3Ui7Op1As"
}

Account, usage and message history

GET /account

The organisation this key belongs to: its plan and billing status, the features it has, and the operations this key may call.

Who am I? The organisation the key belongs to, its plan and whether it is paused, the features it has and exactly which operations this key may call. A good first call to check a key works.

Permission api:manage · MCP tool account_get

No fields: just call it.

What comes back: { organisation, plan, features, key }.

curl "https://arm.adab.solutions/api/v1/account" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "organisation": {
    "id": "Org1Nn2Mm3Bb",
    "name": "Hope Foundation",
    "slug": "hope",
    "timezone": "Europe/London"
  },
  "plan": {
    "id": "growth",
    "name": "Growth",
    "status": "active",
    "trial_ends_at": null,
    "locked": false,
    "seats": {
      "used": 4,
      "limit": 10
    }
  },
  "features": [
    "contacts",
    "lists",
    "donations",
    "api"
  ],
  "key": {
    "id": "Key9Lk2Jh5Gf",
    "permissions": [
      "contacts:read"
    ],
    "operations": [
      "contacts_search",
      "contacts_get"
    ]
  }
}

GET /usage

What the organisation used between two dates (default: this calendar month): messages sent and received by channel, workflow runs, API requests, supporters and storage.

What the organisation used over a period: messages sent by channel and received, automation runs, API requests, supporters and storage. Dates are in the organisation's time zone.

Permission billing:read · MCP tool usage_get

FieldTypeNotes
fromquery string First day; the start of this month if left out.Example "2026-09-01"
toquery string Last day, included; now if left out.Example "2026-09-30"

What comes back: { from, to, supporters, storage_bytes, messages: { sent, sent_total, received }, workflow_runs, api_requests }.

curl "https://arm.adab.solutions/api/v1/usage?from=2026-09-01&to=2026-09-30" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "from": "2026-08-31T23:00:00.000Z",
  "to": "2026-09-30T23:00:00.000Z",
  "supporters": 1840,
  "storage_bytes": 5242880,
  "messages": {
    "sent": {
      "email": 920,
      "whatsapp": 140
    },
    "sent_total": 1060,
    "received": 88
  },
  "workflow_runs": 310,
  "api_requests": 4521
}

GET /messages

The message history, newest first: every email, WhatsApp, SMS, Instagram and Apple message sent or received. Filter by channel, direction, status, supporter or date.

Every message sent or received, newest first, with its status and any error. Use it to check what happened to a message from messages_send, or to read someone's conversation.

Permission messages:read · MCP tool messages_list

FieldTypeNotes
channelquery "email" | "whatsapp" | "sms" | "instagram" | "apple" email, whatsapp, sms, instagram or apple.Example "email"
directionquery "in" | "out" in for received, out for sent.
statusquery "queued" | "sending" | "sent" | "delivered" | "read" | "failed" | "blocked" | "dry_run" | "received" Only messages with this status.
contact_idquery string Only this person's messages.Example "k3Jd9QmZp2Lx"
sincequery string From this day.
untilquery string Up to and including this day.
limitquery integer From 1 to 200. Default 50.Example 10
offsetquery integer From 0 to 9007199254740991. Default 0.

What comes back: { data: [message, …], total, limit, offset }.

curl "https://arm.adab.solutions/api/v1/messages?contact_id=k3Jd9QmZp2Lx&channel=email&limit=10" \
  -H "Authorization: Bearer $ADAAB_API_KEY"
Example response
{
  "data": [
    {
      "id": "Msg3Ui7Op1As",
      "contact_id": "k3Jd9QmZp2Lx",
      "channel": "email",
      "direction": "out",
      "status": "delivered",
      "recipient": "amina@example.org",
      "subject": "Thank you, Amina",
      "body": "…",
      "template_id": "Tpl6Wq9Er3Zu",
      "automation_id": null,
      "broadcast_id": null,
      "error": null,
      "created_at": "2026-10-04T09:31:00.000Z",
      "sent_at": "2026-10-04T09:31:02.000Z"
    }
  ],
  "total": 1,
  "limit": 10,
  "offset": 0
}

We use essential cookies to keep you signed in and remember your settings. With your agreement we may also use cookies that help us understand how the site is used. Read our privacy policy