Documentation

Everything you need to send email with Sankofa: one HTTP request to send, a dashboard to manage senders and keys, and webhooks to follow delivery.

Base URL: https://api.trysankofa.com

Quickstart

You can send your first email in a couple of minutes, before setting up a domain.

  1. Create an account and confirm your email address.
  2. Open API Keys in the dashboard and create a key. It starts with esk_ and is shown once, so save it somewhere safe.
  3. Send yourself an email from the shared test sender. Replace you@yourcompany.com with the email address you signed up with; the test sender can only email that address.
Send a test email
$ curl -X POST https://api.trysankofa.com/v1/emails \
-H "Authorization: Bearer esk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@test.trysankofa.com",
"to": "you@yourcompany.com",
"subject": "Hello from Sankofa",
"text": "My first email."
}'

The response contains a messageId. Check your inbox (and the spam folder the first time). When you're ready to email other people, verify a sender of your own.

Authentication

Every request carries an API key in the Authorization header:

Header
Authorization: Bearer esk_your_api_key
  • Create and revoke keys on the API Keys page. A key is shown once, when you create it.
  • Call the API from your server, never from a web page or a mobile app. Anyone who can see the key can send email as you.
  • Use a separate key for each system, so you can revoke one without breaking the others.
  • A key can do everything in this documentation except create, change or revoke keys. That needs the dashboard.

Who you can send from

The from address of every email must be one your account is allowed to use. There are three options, from quickest to best.

1. The shared test sender

hello@test.trysankofa.com works on every account with no setup. It can only send to your own account email address, one recipient at a time. Use it to try the API.

2. A single address

Confirm that you control one mailbox, such as hello@yourcompany.com, and you can send from exactly that address to anyone. Add it on the Domains page or with the API, then click the link we email to that address.

Add a sender address
$ curl -X POST https://api.trysankofa.com/v1/senders \
-H "Authorization: Bearer esk_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "email": "hello@yourcompany.com" }'
  • Free mailbox addresses (Gmail, Yahoo, Outlook and similar) can't be used as senders. Mail claiming to come from those providers but sent through another service gets rejected or junked.
  • If the address's domain publishes a strict anti-spoofing policy (DMARC set to quarantine or reject), a single address isn't enough and the API answers 422 DomainPolicyError. Verify the whole domain instead.

3. A whole domain

Verifying your domain lets you send from any address on it, and signs your email with your own domain so it passes authentication checks. This is the right setup for production. Add the domain on the Domains page or with the API:

Add a domain
$ curl -X POST https://api.trysankofa.com/v1/domains \
-H "Authorization: Bearer esk_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "domain": "yourcompany.com" }'

The response lists the DNS records to create with your domain provider:

Response
{
  "domain": "yourcompany.com",
  "dkimCnameRecords": [
    {
      "type": "CNAME",
      "name": "abc123._domainkey.yourcompany.com",
      "value": "abc123.dkim.amazonses.com"
    }
    // ...two more
  ],
  "spfTxtRecord": {
    "type": "TXT",
    "name": "yourcompany.com",
    "value": "v=spf1 include:amazonses.com ~all"
  },
  "dnsCheck": [
    { "record": { "type": "CNAME", "name": "...", "value": "..." }, "found": false, "actualValues": [] }
  ]
}
  • Create all three CNAME records. These are what verify the domain.
  • If your domain already has an SPF record (a TXT record starting with v=spf1), add include:amazonses.com to that record instead of creating a second one. A domain must have only one SPF record.
  • dnsCheck shows whether each record is visible yet. DNS changes usually appear within minutes but can take up to a day.

The domain is ready when its verificationStatus is SUCCESS:

Check a domain
$ curl https://api.trysankofa.com/v1/domains/yourcompany.com \
-H "Authorization: Bearer esk_your_api_key"
Response
{
  "domain": "yourcompany.com",
  "verificationStatus": "SUCCESS",
  "dkimStatus": "SUCCESS"
}

Send an email

POST/v1/emails

Send an email
$ curl -X POST https://api.trysankofa.com/v1/emails \
-H "Authorization: Bearer esk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <hello@yourcompany.com>",
"to": ["customer@example.com"],
"subject": "Your order has shipped",
"html": "<p>Your order is on its way.</p>",
"text": "Your order is on its way."
}'
FieldTypeDescription
fromstringrequiredThe sender, as "hello@yourcompany.com" or "Acme <hello@yourcompany.com>". Must be a sender your account can use.
tostring or string[]requiredOne address, or a list of up to 50. Everyone on the list is in the To line and can see the others, so send separate emails when recipients shouldn't see each other.
subjectstringrequiredThe subject line. Not needed when sending a template.
htmlstringoptionalThe HTML body.
textstringoptionalThe plain-text body. Provide html, text, or both; sending both gives the best deliverability.
templateNamestringoptionalSend a saved template instead of subject, html and text. See Templates and bulk sending.
templateDataobjectoptionalValues for the template's variables.
attachmentsobject[]optionalFiles to attach. See Attachments below.

A successful request returns 202 Accepted with the email's id. Keep it if you want to look the email up later or match it to webhook events.

Response
{
  "messageId": "010201a12003b0ff-bb6f1fc0-7a46-4d2f-9549-e60f575cf3a9-000000"
}
  • Each recipient uses one credit and counts once toward the API key's daily limit, if it has one.
  • If a request fails, nothing is charged. Requests aren't de-duplicated, so retrying one that succeeded sends the email again.
  • The request body can be up to 10 MB.

From Node.js

Any language that can make an HTTPS request works. For example, with the built-in fetch in Node.js 18 or later:

JavaScript
const response = await fetch("https://api.trysankofa.com/v1/emails", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SANKOFA_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "Acme <hello@yourcompany.com>",
    to: "customer@example.com",
    subject: "Your order has shipped",
    html: "<p>Your order is on its way.</p>",
  }),
});

const body = await response.json();
if (!response.ok) throw new Error(`${body.error}: ${body.message}`);
console.log(body.messageId);

Attachments

Add an attachments list to the request. Each entry has a filename, the file's bytes as a base64 string in content, and optionally a contentType (worked out from the file name if you leave it out).

JavaScript
import { readFile } from "node:fs/promises";

const invoice = await readFile("invoice.pdf");

await fetch("https://api.trysankofa.com/v1/emails", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SANKOFA_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "Acme <hello@yourcompany.com>",
    to: "customer@example.com",
    subject: "Your invoice",
    text: "Your invoice is attached.",
    attachments: [
      { filename: "invoice.pdf", content: invoice.toString("base64"), contentType: "application/pdf" },
    ],
  }),
});
  • Up to 10 files per email, 7 MB in total before base64 encoding.
  • content must be base64. Plain text or a file path is refused with 400 ValidationError.
  • Executable and script files (such as .exe, .bat and .js) can't be emailed. Put them in a zip file or send a link instead.
  • An email with attachments still uses one credit per recipient.

Templates and bulk sending

A template is an email you save once and reuse. Write {{name}} wherever a value should go, in the subject or the body, and supply the values when you send. Create templates on the Templates page or with the API:

Create a template
$ curl -X POST https://api.trysankofa.com/v1/templates \
-H "Authorization: Bearer esk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "order-shipped",
"subject": "Your order {{order}} has shipped",
"html": "<p>Hi {{name}}, your order {{order}} is on its way.</p>",
"text": "Hi {{name}}, your order {{order}} is on its way."
}'
  • Names use lower-case letters, digits, hyphens and underscores. Variable names use letters, digits and underscores.
  • Give the template an HTML body, a plain-text body, or both.
  • Values are escaped in the HTML body, so a customer name containing < or & shows as typed and can't break your markup.
  • PUT /v1/templates/:name replaces a template and DELETE removes it. An account can have up to 100.

Send one email from a template

Use templateName and templateData instead of subject, html and text:

Send with a template
$ curl -X POST https://api.trysankofa.com/v1/emails \
-H "Authorization: Bearer esk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <hello@yourcompany.com>",
"to": "ama@example.com",
"templateName": "order-shipped",
"templateData": { "name": "Ama", "order": "A-1042" }
}'

Every variable in the template must have a value. If one is missing the API answers 400 TemplateDataError, names it, and sends nothing.

Send to many people at once

POST/v1/emails/bulk

Each recipient gets their own email, with their own values, and can't see the others. defaultTemplateData supplies values for any recipient that doesn't give its own.

Bulk send
$ curl -X POST https://api.trysankofa.com/v1/emails/bulk \
-H "Authorization: Bearer esk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <hello@yourcompany.com>",
"templateName": "order-shipped",
"defaultTemplateData": { "name": "there" },
"recipients": [
{ "to": "ama@example.com", "templateData": { "name": "Ama", "order": "A-1042" } },
{ "to": "kofi@example.com", "templateData": { "order": "A-1043" } }
]
}'

To do this without code, the Bulk send page in the dashboard takes a pasted list or a CSV file.

For a one-off send you don't need to save a template first. Leave out templateName and give the message itself as subject with html, text or both. Placeholders work there too, or leave them out to send everyone the same email.

The response has one result per recipient, in the order you sent them:

Response
{
  "results": [
    { "to": "ama@example.com", "status": "SUCCESS", "messageId": "010201a1..." },
    { "to": "kofi@example.com", "status": "FAILED", "error": "Cannot send to kofi@example.com: address is on the suppression list (reason: BOUNCE)." }
  ]
}
  • Up to 500 recipients per request. Split a longer list across several requests.
  • One recipient failing doesn't stop the others, and a failed recipient isn't charged. Check each status.
  • If credits or the API key's daily limit run out part-way, the remaining recipients are reported as failed with that reason, so you can send just those again later.
  • A large request takes a while to answer, because it waits until every recipient has been tried. Allow about a minute for 500.
  • Bulk sending needs a verified address or domain. The shared test sender can't be used.
  • Only email people who asked to hear from you. See the Acceptable Use Policy.

Track delivery

Look up one email by the id you got when you sent it:

GET/v1/emails/:messageId

Check an email
$ curl https://api.trysankofa.com/v1/emails/MESSAGE_ID \
-H "Authorization: Bearer esk_your_api_key"
Response (shortened)
{
  "messageId": "010201a12003b0ff-bb6f1fc0-7a46-4d2f-9549-e60f575cf3a9-000000",
  "status": "delivered",
  "events": [
    { "eventType": "Queued", "timestamp": "2026-10-09T09:34:35.212Z" },
    { "eventType": "Send", "timestamp": "2026-10-09T09:34:35.378Z" },
    { "eventType": "Delivery", "timestamp": "2026-10-09T09:34:36.901Z" }
  ]
}

status is the furthest the email has got. Delivery events usually arrive within a few seconds of sending.

StatusMeaning
queuedWe accepted the email and handed it to the mail system.
sentIt left for the recipient's mail provider.
deliveredThe recipient's mail provider accepted it.
openedThe recipient opened it (HTML emails only, and only when their mail app loads images).
clickedThe recipient clicked a link in it.
bouncedThe recipient's mail provider refused it. A permanent bounce adds the address to the suppression list.
repliedThe recipient wrote back, and you receive replies with Sankofa.
complainedThe recipient marked it as spam. The address is added to the suppression list.

GET /v1/emails lists your 200 most recent emails, newest first. In that list the id to use for lookups is sesMessageId. The Emails page in the dashboard shows the same information.

Receive replies

When someone answers an email you sent, Sankofa can receive the reply for you. It shows in the Inbox, under the email it answers on the Emails page, and can be sent to your webhooks. This needs a verified domain. Turn it on in the Domains page or with the API:

Turn on receiving
$ curl -X PUT https://api.trysankofa.com/v1/domains/yourcompany.com/inbound \
-H "Authorization: Bearer esk_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "mode": "subdomain" }'
Response
{
  "domain": "yourcompany.com",
  "mode": "subdomain",
  "verified": false,
  "replyAddress": "reply@reply.yourcompany.com",
  "mxRecord": {
    "type": "MX",
    "name": "reply.yourcompany.com",
    "value": "10 inbound-smtp.eu-west-1.amazonaws.com"
  },
  "otherMx": []
}

Add the MX record from the response where you manage your DNS. Once it is in place, verified becomes true (check with GET on the same path) and every email you send from the domain automatically carries the reply address, so replies come to Sankofa.

Two ways to receive

ModeMail arrives atUse it when
subdomainreply.yourcompany.comYour domain already has mailboxes (Google Workspace, Zoho, or any other). They keep working untouched.
rootyourcompany.comThe domain has no email anywhere else. ALL mail for the domain then comes to Sankofa, to any address.

Choosing root for a domain that has mailboxes elsewhere would cut them off, so the API refuses it with 409 ConfirmationRequired until you repeat the request with "confirmNoOtherMail": true. Send "mode": null to turn receiving off.

Read what arrived

List received email
$ curl https://api.trysankofa.com/v1/inbound \
-H "Authorization: Bearer esk_your_api_key"
Response
{
  "retentionDays": 90,
  "messages": [
    {
      "id": "5b2f0c1e-7a4d-4a52-9d0e-3f6c1b8e2a11",
      "fromAddress": "ama@example.com",
      "fromName": "Ama Mensah",
      "toAddress": "reply@reply.yourcompany.com",
      "subject": "Re: Your order has shipped",
      "preview": "Thanks! When will it arrive?",
      "inReplyTo": "010201a12003b0ff-bb6f1fc0-7a46-4d2f-9549-e60f575cf3a9-000000",
      "kind": "reply",
      "spam": false,
      "attachments": [
        { "filename": "receipt.pdf", "contentType": "application/pdf", "size": 48213, "downloadable": true }
      ],
      "receivedAt": "2026-10-12T13:00:15.000Z"
    }
  ]
}
  • GET /v1/inbound/:id returns one message with its full text and html. DELETE /v1/inbound/:id removes it.
  • inReplyTo is the id of the email being answered, when it can be worked out. That email's status becomes replied, and GET /v1/emails/:messageId lists its replies.
  • kind is auto for out-of-office replies, bounce notices and other machine-sent mail, which don't count as a reply.
  • Messages our provider judges to be spam are kept but marked spam. Messages carrying a virus are discarded.
  • Attachments are listed with their name, size and whether they are downloadable. Fetch a file with GET /v1/inbound/:id/attachments/:index, where the index is its position in the list, starting at 0. Files on spam aren't kept.
  • Answer a message with POST /v1/inbound/:id/reply, giving from (an address on your domain) and text. It goes to the person who wrote, threaded under their message with their text quoted, and uses one credit. The Inbox has a reply box that does the same.
  • Received email is deleted after 90 days. Receiving doesn't use credits.
  • If you show a received message's html in your own app, treat it as untrusted and sandbox it.

Webhooks

Instead of asking for an email's status, you can have delivery events sent to your server as they happen. Register an endpoint on the Webhooks page or with the API. You choose the secret; use a long random string and keep a copy, because it isn't shown again.

Register a webhook
$ curl -X POST https://api.trysankofa.com/v1/webhooks \
-H "Authorization: Bearer esk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourcompany.com/webhooks/sankofa",
"secret": "a-long-random-string-you-generate"
}'

For each event we send a POST with a JSON body to your URL:

Webhook body
{
  "messageId": "010201a12003b0ff-bb6f1fc0-7a46-4d2f-9549-e60f575cf3a9-000000",
  "eventType": "delivered",
  "timestamp": "2026-10-09T09:34:36.901Z",
  "recipients": ["customer@example.com"]
}
  • eventType is one of sent, delivered, opened, clicked, bounced, complained or received.
  • timestamp is when the event happened. An email can be opened or clicked more than once, and each time is a separate event.
  • Answer with any 2xx status to acknowledge. Otherwise we try again after 1, 2, 4 and 8 seconds, then give up on that event.
  • The same event can arrive more than once, and events can arrive out of order. Make your handler safe to repeat.

Received email

If you receive replies, each incoming message produces a received event. messageId is the email it answers, or an empty string when it isn't a reply to anything you sent. Fetch the full message with GET /v1/inbound/:id using received.id.

Webhook body
{
  "messageId": "010201a12003b0ff-bb6f1fc0-7a46-4d2f-9549-e60f575cf3a9-000000",
  "eventType": "received",
  "timestamp": "2026-10-12T13:00:15.000Z",
  "recipients": ["reply@reply.yourcompany.com"],
  "received": {
    "id": "5b2f0c1e-7a4d-4a52-9d0e-3f6c1b8e2a11",
    "from": "ama@example.com",
    "subject": "Re: Your order has shipped",
    "automatic": false
  }
}

Check the signature

Every delivery carries an X-Webhook-Signature header of the form t=1791538475,v1=5f2b…. t is the time we signed it (Unix seconds) and v1 is an HMAC-SHA256, in hex, of {t}.{raw body} using your secret. Recompute it and compare before trusting the request:

sankofa-webhook.js
import crypto from "node:crypto";

// rawBody must be the exact bytes we sent, as a string, before any JSON parsing.
export function isFromSankofa(rawBody, signatureHeader, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(signatureHeader.split(",").map((part) => part.split("=")));
  if (!parts.t || !parts.v1) return false;

  // Reject old deliveries so a captured request can't be replayed later.
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > toleranceSeconds) return false;

  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest();
  const given = Buffer.from(parts.v1, "hex");
  return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}

Then call it at the top of your endpoint, before reading the event. For example, with Express:

server.js
import express from "express";
import { isFromSankofa } from "./sankofa-webhook.js";

const app = express();

// express.raw keeps the body as bytes, which the signature check needs.
app.post("/webhooks/sankofa", express.raw({ type: "application/json" }), (req, res) => {
  const rawBody = req.body.toString("utf8");
  const signature = req.get("X-Webhook-Signature") ?? "";

  if (!isFromSankofa(rawBody, signature, process.env.SANKOFA_WEBHOOK_SECRET)) {
    return res.status(401).end();
  }

  const event = JSON.parse(rawBody);
  if (event.eventType === "bounced") {
    // ...stop emailing event.recipients
  }
  res.status(200).end();
});

API keys and daily limits

Any API key can have a daily limit: the most emails it may send in a day. It is the simplest way to cap the damage if a key leaks, a script loops, or an AI agent misbehaves. Set it when you create a key on the API Keys page, or edit it later in the table there.

  • Counted per recipient: an email to three people uses three.
  • Resets at 00:00 UTC.
  • A key with no limit can send as much as your credits allow.
  • Limits can only be changed from the dashboard, so a key can't raise its own.

A send that would go over the limit is refused with 429. Nothing is sent and no credits are spent:

Response
{
  "error": "DailyLimitReached",
  "message": "This API key has reached its daily limit of 50 emails. The limit resets at 00:00 UTC. It can be changed on the API Keys page of the dashboard."
}

AI agents (MCP)

Sankofa has a Model Context Protocol (MCP) server, so AI assistants such as Claude Code and Cursor can send email and check delivery for you. Create an API key just for the agent, give it a daily limit, and connect:

Connect Claude Code
$ claude mcp add --transport http sankofa \
https://api.trysankofa.com/mcp \
--header "Authorization: Bearer esk_your_api_key"

Clients that take a JSON config (Cursor and others) use the same URL and header:

MCP config
{
  "mcpServers": {
    "sankofa": {
      "type": "http",
      "url": "https://api.trysankofa.com/mcp",
      "headers": {
        "Authorization": "Bearer esk_your_api_key"
      }
    }
  }
}
ToolWhat it does
send_emailSends one email from a sender your account has verified.
get_email_statusReturns an email's status and delivery events.
list_emailsLists recent emails with their status.
get_credit_balanceReturns your credits and how much of the key's daily limit is used.
list_sendersLists the addresses and domains the agent can send from.
  • The server only accepts API keys, so the key's daily limit always applies to what the agent sends.
  • An agent can't verify senders, create or change keys, or buy credits.
  • The server asks agents to confirm the recipient and content with you before sending. That is a request to the agent, not a guarantee; the daily limit is the hard stop.

Credits

Sending is prepaid. One credit sends one email to one recipient, and credits cost GHS 5 per 1,000. Buy them by card or mobile money on the Billing page. There is no subscription, and credits are returned automatically if an email fails to send.

Check your balance at any time:

Check credits
$ curl https://api.trysankofa.com/v1/billing \
-H "Authorization: Bearer esk_your_api_key"

The response includes emailCredits. When a send needs more credits than you have, the API answers 402 InsufficientCreditsError and sends nothing.

Suppression list

Addresses that permanently bounce or report an email as spam are added to a suppression list, and later sends to them are refused with 422 SuppressedRecipientError before any credit is spent. This protects delivery for everyone who sends through Sankofa. If an address was suppressed by mistake, write to info@trysankofa.com.

Errors

Errors use ordinary HTTP status codes and always have the same JSON shape. error is a stable name you can check in code; message is written for a person.

Error response
{
  "error": "ValidationError",
  "message": "from, to, and subject are required"
}
StatusErrorWhat it means
400ValidationErrorSomething in the request is missing or malformed. The message says what.
400MessageRejectedThe mail system refused the message itself, for example because of an attachment it doesn't allow. Nothing was charged.
400TemplateDataErrorA variable the template uses has no value. The message names it.
401UnauthorizedThe API key is missing, wrong or revoked.
402InsufficientCreditsErrorNot enough credits for this send. Nothing was sent.
403ForbiddenThe sender isn't verified on your account, the test sender was used for someone else's address, or the action needs the dashboard.
404NotFoundNo such email, domain, sender or webhook on your account.
409ConflictThe domain or address is already in use by another account.
413PayloadTooLargeThe request is too large. A send can be up to 10 MB including attachments.
422SuppressedRecipientErrorThe recipient is on the suppression list. Nothing was sent or charged.
422UnverifiedDomainErrorThe sending domain hasn't finished verification yet.
422DomainPolicyErrorThe address's domain requires domain verification rather than single-address verification.
429DailyLimitReachedThe API key's daily limit is used up. Nothing was sent or charged.
429DailyQuotaExceededErrorSankofa as a whole is at its sending capacity for the day. Try again later.
500InternalErrorSomething went wrong on our side. Credits for an email that didn't go out are returned automatically.

Endpoint reference

All paths are relative to https://api.trysankofa.com and need the Authorization header.

MethodPathWhat it does
POST/v1/emailsSend an email
GET/v1/emailsList your 200 most recent emails
GET/v1/emails/:messageIdStatus and delivery events for one email
POST/v1/emails/bulkSend a template or a one-off message to up to 500 recipients
GET/v1/templatesList your templates
POST/v1/templatesCreate a template
GET/v1/templates/:nameGet one template
PUT/v1/templates/:nameReplace a template
DELETE/v1/templates/:nameDelete a template
GET/v1/test-senderThe shared test sender and the one address you can email with it
GET/v1/sendersList your single sender addresses
POST/v1/sendersAdd a sender address and email it a confirmation link
DELETE/v1/senders/:emailRemove a sender address
GET/v1/domainsList your domains
POST/v1/domainsAdd a domain and get its DNS records
GET/v1/domains/:domainVerification status of a domain
GET/v1/domains/:domain/inboundReceiving status of a domain and its DNS record
PUT/v1/domains/:domain/inboundTurn receiving on or off for a domain
GET/v1/inboundList received email
GET/v1/inbound/:idRead one received email in full
GET/v1/inbound/:id/attachments/:indexDownload an attachment of a received email
POST/v1/inbound/:id/replyAnswer a received email
DELETE/v1/inbound/:idDelete a received email and its attachments
GET/v1/webhooksList your webhook endpoints
POST/v1/webhooksRegister a webhook endpoint
DELETE/v1/webhooks/:idRemove a webhook endpoint
GET/v1/api-keysList your API keys with their limits and today's usage
GET/v1/billingCredit balance and pricing
POST/mcpMCP server for AI agents

Something missing or unclear? Write to info@trysankofa.com and we'll help. Use of the API is covered by our Terms of Service and Acceptable Use Policy.