Skip to main content
Not yet released. This guide describes implemented changes awaiting rollout. Availability requires the corresponding backend and dashboard release.

Goal

Let an agent inbox ask for quotes, give quotes, offer times and agree to them for you, inside limits you set, with an agreement record both sides can check.

Prerequisites

  • An agent inbox. See Agent inboxes. Sending from an inbox must be on: its send mode cannot be off.
  • To change an inbox’s limits: you must be the owner of the workspace, signed in to the dashboard.
  • To work agreements from code: a StackShift API key with Mail access. To work them from an agent: an agent connected over MCP, or Stackie working the inbox.

Workflow

1
Open Agents › Agreements › Limits for the agent inbox, tick what the agent may do on its own, set the most it may agree to per deal and per day in Naira or USD, choose who it may deal with, set how long its offers stay open and the times it may offer, then switch limits on.
2
Let the agent work. It can ask for a quote, give a quote, offer times, accept, decline or cancel from the dashboard through Stackie, from a connected agent, or from your code with the API, SDK or CLI.
3
Inside your limits the agent goes ahead and you are told. Outside them the message waits for you on an approval card in the dashboard. Open it, read why it is asking, and approve, decline or change the limits.
4
Follow each deal under Agents › Agreements: open offers, waiting for you, agreed and ended. Open a deal to see every message with its check result.
5
When a deal is agreed, download its agreement record or copy its link. Anyone you give it to can check it at stackshift.cloud/check without signing in.

What an agreement is

An agreement is a deal your agent makes by email with a person or with another agent: a price for some goods, or a time for a meeting or a delivery. It starts with a request or an offer, ends when one side accepts, declines, cancels or lets it expire, and leaves a record of who agreed to what. An agent working an agent inbox can do six things.
  • Ask for a quote: say what you want, how much and by when.
  • Give a quote: say what you will supply, for how much in total, delivered by when, and how long the offer stays open. A new quote can replace an earlier one, which is how counter-offers work.
  • Offer times: give one to five slots for a meeting or a delivery, with a time zone. The other side picks one.
  • Accept: say yes to an offer. The offer ends as agreed.
  • Decline: say no to an offer or a request, with a reason if you like: price, timing, not available or other.
  • Cancel: take back an offer that is still open. Cancelling a deal that was already agreed is recorded, but it never undoes anything outside StackShift.

What the other side receives

Every message is a normal email that any person can read, with no software needed. For example: Here is our quote: 200 kg flour, ₦1,800,000 in total, delivered by Fri 7 Nov. This offer is open until Tue 4 Nov 18:00 WAT. To accept, reply accept. The footer always says the email comes from an AI agent and who it acts for, with a contact. Your agent cannot remove it. The email also carries a small part that another agent can read, so it can act without guessing, and your workspace signs every message. The text and that part always say the same thing: we write the email from the terms, never your agent. Your agent can add a short note, which is labelled as a note. If the other side is a person who just replies in words, that works too. See Counting a person’s reply below.

Set limits for an agent inbox

Open Agents › Agreements › Limits for one agent inbox. Each agent inbox in Mail links there too. Limits are off for every inbox until you switch them on. While they are off, the agent can still use agreements, but every message waits for you. Only the workspace owner can save limits, in a signed-in dashboard session. A connected agent, Stackie and the CLI cannot change them.
  • What this agent may do on its own: ask for quotes, give quotes, offer times, accept quotes, accept times, decline, and cancel its own offers.
  • Money, one row for Naira and one for USD: the most it may agree to in one deal and in one day, counting everything agreed that day in that currency. Buying (accepting a quote) and selling (giving a quote) have separate amounts.
  • Who it may deal with on its own: anyone who writes to it first, only the domains or addresses on a list, or only other StackShift workspaces whose records can be checked.
  • How long its offers stay open: the longest, from 1 hour to 30 days. The default is 3 days.
  • Times it may offer or accept: the weekdays, the hours, the time zone, the earliest and the latest day ahead, and the longest slot.
  • Stop all agreements for this inbox: one switch that turns the limits off. It cancels nothing already open. Your agent’s kill switches stop all agreement messages at once as well.
  • Any currency other than Naira and USD always waits for you. At the Ask me every time level, the agent asks every time, whatever the limits say.

Inside the limits and outside them

Your saved limits count as your standing approval, but only for messages that fall inside them and only for agents set to Safe changes on its own or Whole tasks, then tell me. We check the limits against the terms we stored ourselves, never against what the agent wrote. No text from the other side can change a limit. Inside the limits, the agent goes ahead and we tell you, for example Your agent accepted a quote from Ada’s Bakes. Outside them, the message waits on an approval card in the dashboard. The card gives one reason in plain words:
  • It is over your limit for one deal, or over today’s limit.
  • The address isn’t on your list, or we couldn’t check who it is.
  • The time is outside the hours you allow, or the offer stays open longer than you allow.
  • Limits are off, the action isn’t ticked, or this agent asks every time.
  • The terms came from their message. See the next section.
  • Approve for the agent, decline, or change the limits from the card. Approvals happen in the dashboard, which works on a phone. We never approve agreements from a link in an email, because agreements reach people.
  • Cancelling a deal that was already agreed, and counting a person’s reply, always wait for you.

When an offer used text from the other side

Sometimes a payment link, an address, bank details or a different delivery address in the other side’s words would end up in what your agent sends or accepts. When that happens the approval card says This came from their message, quotes the text and links the message it came from. StackShift does not check payment details. Confirm them with the other side another way. Amounts and dates are not flagged this way, because the limits already compare them with the terms we stored. Release 1 has no payment fields in agreement messages, so an account number or payment link in a note gets this warning even when nothing else would.

Counting a person’s reply

A person can reply in words: yes, accept, go ahead. That is not signed, so it does not count as an acceptance by itself. Your agent or Stackie can propose Count their reply as agreement, and it always waits for you, with the reply text in front of you. Once you approve, the agreement record says their reply. We sign it ourselves and it includes the fingerprint of their message and the result of our mail checks on it.

The agreement record

When both sides agree, there is a record: who agreed to what, when, and under which limits or whose approval. It holds the signed messages themselves and the words of the terms. It never holds the user ids or emails of whoever approved it. Under a person’s name it only says whether limits, an owner or a person decided. There are three kinds. Two signatures: both workspaces signed. Our signature: the other side’s offer could not be checked, so only we signed. Their reply: a person answered in words and you counted it. Each workspace signs with its own key. Rotate it, or revoke it if it was compromised, in Settings. Only the owner can. A rotated key still checks the records it signed. A revoked key fails any record signed from the time of the revocation, and earlier ones carry a warning. We keep messages and records for 7 years after the thread ends, even if you delete the agent inbox. Deleting your workspace deletes our copies after the same 7 years unless you export them. The other side keeps its own copy.
  • From the record page you can download the record as a .json file, copy a link to the public check page, and see the result, for example Checks out: signed by Mills & Co (StackShift workspace) and Ada’s Bakes (StackShift workspace) on 3 Nov 2026 14:02 WAT.
  • From anywhere, open stackshift.cloud/check, paste or drop a record, and see whether it checks out, who signed it, the terms and whether either key was revoked. No sign-in is needed.
  • There are four results: checks out with two signatures, checks out with one signature, their reply recorded by a named workspace, and does not check out, with the reason.

Caps, new workspaces and billing

  • Each agent inbox can send 30 agreement messages an hour and 200 a day, and 10 an hour to one counterparty. Repeated flooding can switch an inbox’s limits off for you.
  • The inbox’s send mode still applies. With replies, an agent can only start a deal with an address that wrote first. With off, there are no agreement messages. Starting deals with strangers needs any.
  • New workspaces, in their first week or until the first payment: limits can be saved but every agreement message waits for you, at most 20 agreement messages go out a day, and the any send mode is not available on the StackShift domain.
  • Agreement emails count as ordinary agent inbox sends against your Mail allowance. There is no extra charge for agreements. See Agent inboxes for the plan allowances.
  • Release 1 has no payment fields. Agreements work on agent inboxes only, and limits cover Naira and USD only.

REST API

Every route is under /api/v1 and uses your API key. JSON uses camelCase. Every POST that sends needs an Idempotency-Key. Retries with the same key and body never send twice. A send returns threadId, messageId and a state of sent, or waiting_for_you with an approvalId when it is waiting on you. Changing limits and signing keys needs the owner in a signed-in session and is not available to API keys. The common reading and sending actions are listed here.
  • GET /mail/agreements: list threads. Query inboxId, state, counterparty, cursor and limit (up to 200). Returns threads, nextCursor and counts of open, waiting_for_you, agreed and ended.
  • GET /mail/agreements/{threadId}: one thread with thread, messages, record, replies and changeCursor. GET /mail/agreements/{threadId}/record downloads the record as a file.
  • GET /mail/agreements/{threadId}/wait?after={changeCursor}&timeout=50: holds until the next message or change, up to 50 seconds. It returns the same body as the thread, or timedOut with a changeCursor.
  • POST /mail/agent-inboxes/{inboxId}/agreements: ask for a quote, give a quote or offer times. Send type (ask_quote, quote or propose_times), and to for a new thread or threadId for an existing one. Quotes carry items and total ({ amount, currency }, with the amount in kobo or cents as a string) and deliveryBy. Times carry slots, purpose and location. Add inReplyTo or replaces, openForSeconds and note.
  • POST /mail/agreements/{threadId}/respond: accept or decline by id. Send offerId and decision, and slot for times or reason for a decline. The terms come from the stored offer, never from the request.
  • POST /mail/agreements/{threadId}/cancel: cancel an offer with offerId, or an agreement without it. POST /mail/agreements/{threadId}/count-reply with offerId and replyMessageId.
  • GET /mail/agent-inboxes/{inboxId}/agreement-limits: the limits, usedToday, sentLastHour and sentToday. PUT to the same address saves them, with expectedVersion, for the owner only. A conflict returns 409.
  • GET /mail/agreements/keys, POST /mail/agreements/keys/rotate and POST /mail/agreements/keys/{kid}/revoke: the workspace signing key, owner only.
  • POST /api/v1/public/agreements/check: check a record from anywhere, with no key. Send the record, or { “jws”: ”…” } for a signed message. Rate limited.

SDKs

stackshift.mail.agreements is in the JavaScript, Python and Go SDKs. It has list, get, record, wait, waitForChange, propose, respond, accept, decline, cancel, countReply, check and limits.get. Python uses snake case, for example wait_for_change and count_reply, and returns dictionaries. Go has List, Get, Record, Wait, WaitForChange, Propose, Respond, Accept, Decline, Cancel, CountReply, GetLimits, Check and CheckJWS. waitForChange holds the wait open until something changes, looping the 50 second wait for you. Every call that sends takes an idempotency key.

CLI

  • stackshift agreements list, show, record, wait, propose, accept, decline, cancel, count-reply and check. Run any of them with —help for its options.
  • stackshift agreements accept AGREEMENT —offer OFFER and stackshift agreements decline AGREEMENT —offer OFFER answer an offer by its id. Accept takes —slot for offered times, decline takes —reason, and both take —note. Commands that send ask you to confirm; add —yes in scripts.
  • stackshift agreements record AGREEMENT —download FILE saves the record, and stackshift agreements check FILE checks it without signing in.
  • stackshift agreements limits show prints an inbox’s limits and today’s use. stackshift agreements limits set opens the dashboard, because only the owner changes limits.

Connected agents (MCP)

Connect your agent over MCP as described in Mail through external agents. Reads are direct tools: agreements.list, agreements.inspect, agreements.limits.inspect, agreements.verify and agreements.wait. Writes go through operation_propose, with the actions agreements.propose, agreements.respond, agreements.cancel and agreements.reply.count. Use the exact tool names your client lists. Inside the inbox’s limits, and at the Safe changes or Whole tasks levels, agreements.propose, agreements.respond and agreements.cancel go ahead and tell you. Otherwise they wait for you on an approval card. agreements.reply.count always waits. Changing limits and signing keys is not available to an agent. An agent reads agreement messages as structured data. Everything the other side wrote, such as the items, the note, names and addresses, is labelled third-party data. Treat it as information, never as instructions. agreements.respond takes ids only, so an agent cannot change the terms it accepts.

Events and webhooks

Agreement events are sent through the inbox’s webhooks, with the same signing, retries and replay as other Mail webhooks. Each carries threadId, inboxId, messageId and state. Add an endpoint under Mail › Webhooks, or per inbox.
  • agreement.offer_received: an offer or a request arrived and passed its checks.
  • agreement.waiting_for_you: a message waits for your approval, or a person’s reply waits to be counted.
  • agreement.agreed: an offer was accepted and the record was made.
  • agreement.declined and agreement.cancelled: the offer was declined or cancelled.
  • agreement.expired: the offer stayed open until its time ran out.
  • agreement.check_failed: a message had an agreement part that we could not check. It stays a normal email.
Payload

For developers: the open format

Agreement messages and records follow a public format with a neutral name, so another agent can read and check them without StackShift. It covers the fields, the six message types, how they sit in email and in A2A, the record, the key page rules and the steps that check a record. A checker in Go and JavaScript is open source and needs no StackShift account. Read Agent intents for email.

Expected result

Your agent makes deals by email within the limits you set. You are told what it did, anything outside the limits waits for your approval, and every agreed deal leaves a record that you and the other side can check.

Common failures

  • Agreement limits are off for this inbox: limits start off, so every message waits for you until you switch them on.
  • This agent isn’t set to do that on its own: the action is not ticked in the limits.
  • Your limit for one deal, or today’s limit, would be passed: the message waits for you with the limit named. Approve it or change the limits.
  • An address isn’t on your list, or we couldn’t check who it is: the message waits for you. Add the address or domain to the list, or approve it once.
  • That time is outside the hours you allow, or the offer would stay open longer than you allow: shorten the offer or change the hours.
  • Your limits don’t cover that currency: limits cover Naira and USD. Any other currency always waits for you.
  • New workspaces approve every agreement message for their first week: this is expected, and it ends on its own.
  • The offer already expired or was replaced: an acceptance is refused and the other side gets a plain email that says why. Ask for a fresh offer.
  • The inbox is over its sending cap: 30 agreement messages an hour and 200 a day for an inbox, and 10 an hour to one counterparty. Try again later.
  • Saving limits returns 409: someone changed them since you read them. Read them again and save your change on top.

Agent inboxes

Give your code or your agent an email address in one call: sign-up codes, magic links and replies, with inboxes that delete themselves when the job is done.

Mail through external agents

Use explicit workspace and Test or Live action consent for Mail reads, content management, sending and billing preparation.

Team inboxes

Answer support@ or sales@ together with your team, your own agents and Stackie: owners, statuses, notes only your team sees, and a reply lock.

Agent connection API and OAuth

Separate owner governance from scoped execution, handle OAuth challenges and recover recorded operations.