GraphQL API
Your developer names the fields they want (conversations, outcomes, seekers, responder coverage) and Echo returns exactly those, on whatever schedule you run it. One token, scoped to your account and nobody else's.
$100 a month on top of any plan. What that includes
{
conversation(
startDate: "2026-07-01T00:00:00Z"
endDate: "2026-07-31T23:59:59Z"
dateField: "closed_at"
) {
totalCount
nodes { topic outcomeTitle }
}
}
{
"data": { "conversation": {
"totalCount": …,
"nodes": [
{ "topic": "…",
"outcomeTitle": "…" }
]
} }
}
Start here
If you are not a developer, this page can look like a wall of code. The idea underneath it is simple, and it is worth understanding before you decide whether your ministry needs it.
That is all an API is, a way for one system to ask another system a question and get an answer back.
Your team signs into Echo through a screen. Another program signs in through an API: the same data, the same permissions, no human clicking.
Most exports hand you every column and leave you to find the three you needed.
With GraphQL your developer names the fields (the topic, the outcome, the responder) and those are the fields that come back. Smaller, faster, and nothing to clean up at the other end.
Once it is set up it runs on its own: your systems pull from Echo every night, every hour, or the moment somebody opens the dashboard.
What your board looks at is the live record rather than the export somebody downloaded three weeks ago.
The two gaps
What you get back is one report instead of two, and a number your board can trust because nobody typed it twice.
The moment Echo stops being the whole picture. A seeker reaches out on WhatsApp, a responder walks with them for three weeks, they get connected to a local church, and then the story continues in your CRM, your discipleship tracker, or the spreadsheet your regional director keeps.
Right now somebody rekeys it, or it never crosses over at all.
The other moment is the report nobody enjoys assembling. Your board, or a funder, wants Echo’s outcomes sitting beside giving, events or field data, and getting there means one person exporting out of two systems every quarter and joining them by hand.
This add-on closes both gaps. Conversations, outcomes, seekers and responder activity move into the system that already holds the rest of the story, on a schedule, without a person in the middle.
What is in there
Everything below is scoped to your ministry and nobody else's. A token can only ever see the data of the account it was made in.
Be honest with yourself first
It is a real add-on with a real price. We would rather you skip it than buy it and never switch it on.
Not sure which one you are? Tell us what you are trying to join up
GraphQL API
per month, added to whichever plan you are on
One price, whatever your plan. It covers up to 5 GraphQL-enabled tokens, 300 requests a minute, and the whole schema, reads and writes. No setup fee, no per-query charge, no minimum term, and no negotiation about which endpoints you are allowed to touch.
Already a customer? Ask us to switch it on
Some ministries run at a scale the standard limits were not drawn for: more tokens, a higher rate, wider date windows, or capacity set aside for their traffic alone. That is a custom arrangement and we price it per ministry rather than pretending one number fits.
Getting started
Four steps. There is no build, no integration project and no setup fee.
Ask us, or say yes at signup. It goes live on your account the same day, and nothing about your existing plan changes.
An admin creates one in Echo’s admin under API Keys. It is shown once, so copy it then. Treat it like a password, give each system its own rather than sharing one, and deactivate it the moment a contractor’s work ends.
Everything goes to one address by POST, with the token in the Authorization header. If it comes back with your ministry’s name, the connection is good.
Results come back in pages with a cursor. Ask for the next page with that cursor until there is not one, and store the last updatedAt you saw so tomorrow’s run only fetches what changed.
# Your Echo address is the one your team signs in at.
curl https://your-ministry.echoglobal.org/graphql \
-H "Authorization: Bearer $ECHO_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"{ tenant { name timeZone } }"}'
# {"data":{"tenant":{"name":"Your Ministry","timeZone":"UTC"}}}
Worked examples
Real queries against the live schema, with the shape of the answer beside them. Values are illustrative; the fields and arguments are not.
The question every board asks and nobody enjoys assembling: how many people did we walk with, what happened, and who did the walking. One request, filtered on the date each conversation was closed rather than started, so a conversation that ran across the month boundary lands in the month it finished.
totalCount gives you the headline number without pagingdateField chooses which timestamp the range applies toreferrerId to split the same figure by channel or campaignquery ClosedLastMonth {
conversation(
startDate: "2026-07-01T00:00:00Z"
endDate: "2026-07-31T23:59:59Z"
dateField: "closed_at"
status: ["closed"]
first: 100
) {
totalCount
nodes {
id
topic
closedAt
outcomeTitle
user { fullName }
referrer { name category }
tags { name }
}
pageInfo { hasNextPage endCursor }
}
}
The pattern behind every scheduled sync. Filter on updated_at, keep the newest timestamp you saw, and pass it back as the start of tomorrow’s window. A conversation that was reopened, retagged or given an outcome after it closed comes through on the run that follows, which a filter on the creation date would miss entirely.
after: endCursor until hasNextPage is falsequery ChangedSince(
$since: ISO8601DateTime!
$until: ISO8601DateTime!
$after: String
) {
conversation(
startDate: $since
endDate: $until
dateField: "updated_at"
first: 250
after: $after
) {
nodes {
id
status
updatedAt
closedAt
outcomeTitle
client { id name email phone country }
}
pageInfo { hasNextPage endCursor }
}
}
Somebody in your CRM has an email address or a phone number, and you want to know whether Echo has been talking to them. Look them up by either, and pull back the outcomes recorded on them over time, so a follow-up team can see the whole relationship instead of the last message.
outcomes returns each outcome with the number of times it was recordedquery FindSeeker {
client(email: ["maria@example.org"]) {
nodes {
id
name
email
phone
country
language
conversationsCount(
startDate: "2026-01-01T00:00:00Z"
endDate: "2026-08-01T00:00:00Z"
)
outcomes { value count }
tags { value count }
}
}
}
The API reads, and in a few places it writes. Your discipleship tracker marks a step complete and Echo sends the message on the channel that seeker already uses: from your ministry’s account, through a named responder, logged where your team can see it. You can also create and update seeker records, keep your tag list in step, and claim or close a conversation exactly as a responder would from the inbox.
mutation SendFollowUp {
sendMessage(input: {
clientId: "48219"
referrerId: "12"
userId: "77"
body: "Hi Maria, you’re on our hearts this week."
}) {
success
errors
message { id status createdAt }
}
}
The details a developer will ask for
Everything below applies to the $100 plan. If your integration needs more than these allow, talk to us.
POST /graphql on your ministry's own Echo addressAuthorization header: Bearer <token>. No cookies, no session, no OAuth dance/graphql-docs on the same address, generated from the live schema, for signed-in stafffirst, after, pageInfo, totalCount, plus nodes if you would rather skip edges429, naming your limit, and the next minute starts a fresh windowerrors array, with a message that says which limit you crossed and what to do insteadQuestions
Not really, and we would rather say so than take the money. The $100 is for access, not for us writing the integration. Somebody on your side, a contractor, or an IT partner has to write the query.
No, and the difference matters. An integration is a specific connection we build and maintain between Echo and a named platform. This is raw access. It can do far more, and it needs somebody on your side to write it. If a ready-made integration covers your case, use that instead.
Both. Eight mutations write: send a message, create or update a seeker, claim or close a conversation, and create, update or delete a tag. Everything else reads. A token can also be made read-only, which blocks every write on it whatever query is sent.
Yes. The message query returns the text, because a ministry joining its own conversations to its own systems usually needs it. It is worth deciding deliberately who holds a token that can: a token can be pinned to a named list of queries, so one issued to an outside partner can be given counts, outcomes and timings without message bodies.
Thirty-one days in a single request. A longer history is a loop over months, which also keeps each response a size your own system can handle. If your integration genuinely needs wider windows, that is what the custom arrangement above is for.
No. Every query is scoped to the account its token belongs to, in the code path itself rather than by a filter somebody remembered to add. A token issued in your account can only ever return your account’s data. See Security & trust.
Deactivate it in Echo’s admin and it stops working immediately. This is why the plan includes five: give each system and each contractor their own, and revoking one never breaks the others. A token can also be made read-only, or restricted to a named list of queries.
The request comes back with a 429 and a message naming your limit, and the next minute starts a fresh window. Nothing is charged for it and nothing is cut off. A sync that hits the ceiling just needs to space its requests out.
It should not. Reads run against a replica of the database rather than the one the inbox uses, and the rate limit and date-range caps exist so that no single query can run away with the account.