May I?

One request, held for 00:00

An agent is waiting to hear back from you.

It has everything it needs to issue the refund. It is not going to. May I? holds the whole request, the exact action and the evidence behind it, until one authenticated human reads it and answers.

Start freeRead the docs

Open source · Apache-2.0 · MCP-native · hosted or on your own metal

What she is looking at

action
payments.refund
amount
USD 48,000.00
destination
acct_8841 — merchant of record
because
"Duplicate capture on order #99812. Both charges settled. Customer has been billed twice."
evidence
settlement.pdf, ticket-4417.png
asked by
billing-ops, over MCP, under a consent you can revoke

She approves. The receipt she gets back is bound to this action, these two files, her identity, and the policy as it read at that moment. Your executor verifies the receipt and only then moves the money. If the agent edits the amount or swaps a file afterwards, the receipt stops validating, and nothing happens.

The action is exact.

You see the specific call the agent intends to make, with every argument that carries weight, as it will be made. Nobody is summarising anything for you.

The evidence is frozen.

The PDFs and images behind the case are hashed the moment the request goes pending. You review the ones the receipt will name.

The person is named.

It arrives as a notification on one approver's phone, or in the browser if they are not on the app. Same request, same review, either way.

Nothing has run.

May I? never performs the action. It is holding a question. The hand on the lever stays yours.

The whole of it

Your agent asks. Your executor checks. We hold the middle.

The agent submits a request to May I?, which holds it until an approver answers; the signed receipt goes back to the agent, which presents it to your executor, which verifies it before performing the action. HELDYour agentover MCPMay I?action + evidence, frozennotify · review · signOne named humanYour executorholds the keysmay i?signed receiptno credentials, no execution, ever cross this line
May I? sits beside the work rather than in front of it. It never holds your credentials and never calls your API. The only thing it hands back is a signature, and a signature is safe to lose.

Two calls, both small

One line to ask. One line to trust.

The agent side · MCP

const receipt = await mayi.ask({
  action: "payments.refund",
  args: { amount: 4800000, to: "acct_8841" },
  because: "Duplicate capture on #99812.",
  evidence: [settlementPdf, ticketPng],
  approver: "dana@northwind",
  deadline: "30m",
});
// blocks here. no polling, no webhook to run.
// resolves approved, denied, or expired.

The executor side · verify

app.post("/refund", async (req, res) => {
  const ok = await mayi.verify(req.body.receipt, {
    action: "payments.refund",
    args: req.body.args,
  });
  if (!ok) return res.status(403).end();

  await stripe.refunds.create(req.body.args);
});
// the args are in the signature. lie and it fails.

Verification is offline: a public key and a hash comparison. Your executor does not call us to check a receipt, so if May I? is down, work that is already approved still runs. Only new questions wait.

The receipt

Proof of what she saw, not proof that she clicked.

  • action

    payments.refund

    The call and every argument that carries weight.

  • evidence

    2 files · sha256

    The exact bytes she opened. Filenames are easy to reuse; bytes are not.

  • approver

    dana@northwind

    Authenticated at the moment of the answer.

  • policy

    rev 41

    The rules as they read on the day she answered.

An audit log says an approval happened. A receipt says what was approved, and refuses to say it about anything else. Most of the value is in that second half.

Try to move a decimal

action
payments.refund
amount
USD 48,000.00
digest
a91f…7c02
receipt valid — signed by dana@northwind

The digest is over the action and its arguments. Change one character and every executor holding the public key knows independently.

Three ways it goes wrong without one

The quiet edit

The agent gets approval for USD 480, then issues USD 48,000. The amount is inside the signature. Your executor rejects the receipt before the payment processor ever hears about it.

The swapped file

Approval was granted against one settlement report. A different PDF shows up at execution time. The hash in the receipt names the first one, so the second one has no approval attached to it.

The replay

A receipt for last Tuesday's refund is presented again this Tuesday. It is single-use and scoped to one request id. The second attempt is not an approval, it is a duplicate.

Put it in front of anything you would not want done twice.

Move money

Refunds, payouts, credits, anything with a settlement on the other side.

Ship code

Merges to protected branches, production deploys, migrations that drop things.

Touch a customer

Account closures, plan changes, anything that sends mail in your name.

Grant access

Role changes, key issuance, adding a principal to something that matters.

Delete

Records, buckets, tenants. The category where undo is a support ticket.

Sign

Contracts, invoices, anything where your name goes on the bottom of the page.

The rule of thumb: if a mistake there costs you a phone call, an apology, or a lawyer, it is worth thirty seconds of someone's attention first.

Hosted

Start asking in an afternoon.

Push notifications, the apps, key rotation, and the pager are ours. Free while you are small enough that a person can still read every request.

Start free

Self-hosted

One service and a database.

Same code, your infrastructure, your signing keys, no request ever leaving your network. Apache-2.0, and the receipts verify the same either way.

Read the source

Asked before

Does May I? need access to my systems?+

No. It holds requests and issues receipts. Your executor already has the credentials and keeps them; it just checks a signature before it runs. May I? never gets a key to the thing it is protecting.

What happens if nobody answers?+

The request expires on the deadline the agent asked for, and expiry is a denial. Silence never becomes a yes.

Can an approver be tricked by a persuasive agent?+

The agent writes the because line, and that is the one field it fully controls. The call, the arguments and the file hashes are all rendered from the request itself, so the persuasion has nowhere to reach. She reads the action, not the argument for it.

How fast is it?+

The request is pending within a few hundred milliseconds and the notification lands about as fast as any other push. The rest of the latency is a human reading, which is the part you are paying for.

What if I want it on my own infrastructure?+

Run it there. Apache-2.0, one service and a Postgres database. The hosted version is the same code with the pager on our side.

The pause is the product.

Hosted, or self-hosted on your own infrastructure. Either way, May I? holds the question and keeps the proof, and never touches the thing itself.

Open the appDocs