Services
A service is how a node reaches something outside the schema. It is the only way an agent can reach anything at all — and it is deliberately narrow: a fixed address, a fixed list of operations, a credential the app can never read back, and a person standing in front of anything that matters.
- What a service is
- The catalogue
- Adding one
- Credentials
- Signing in
- What needs a person
- Payments
- AI-drafted services
- Describing an API yourself
- Receiving from outside
- Connections held open
- Watching for a change
- Every rule a call passes
- When something goes wrong
What a service is
A service belongs to a node. It has an address, a way of authenticating, and a list of operations — the specific things it can do, each with a name and the parameters it takes. An agent on that node may call those operations and nothing else. It cannot compose a URL, cannot call an operation nobody declared, and cannot reach a service on a different node.
There are three ways to get one:
- A built-in family — Stripe, Slack, GitHub and the rest. Pick it and it is ready; the address and the operations come with it.
- Described to the AI — say what you want to connect to and a definition is drafted for you to check and save.
- Typed in yourself — an address and the operations you declare by hand.
The catalogue
These are built in. Choosing one of them is always better than describing the same API yourself: the address and the operations are ours to keep right, and the rules that make them safe travel with them.
In the app, the catalogue is grouped by what each family is for, and there is a search — look for "refund", "send an email" or "text message" rather than for a product name.
Stripe — Payments
Take and refund payments, and read what has been paid. Needs a Stripe secret key
(sk_test_… or sk_live_…). Read Payments before using it.
| Operation | What it does | |
|---|---|---|
| Check the balance | What is available and pending. | read |
| List recent payments | The most recent payments, newest first. | read |
| Look up one payment | One payment by its id. | read |
| Take a payment | Charges an amount. | moves money |
| Refund a payment | Gives money back, in whole or in part. | moves money |
Slack — Messaging
Read a channel and post to it. Needs a Slack bot token (xoxb-…) from your app's OAuth &
Permissions page, invited to the channels you want.
| Operation | What it does | |
|---|---|---|
| List channels | The channels this token can see. | read |
| Read a channel | The most recent messages in one channel. | read |
| Post a message | Says something in a channel, as the app. | messages people |
GitHub — Code
Read issues and pull requests, open issues, and comment. Needs a personal access token
(github_pat_… or ghp_…) with access to the repositories you name.
| Operation | What it does | |
|---|---|---|
| List issues | Issues in a repository, filtered by state or label. | read |
| Look up one issue | One issue, with its body and state. | read |
| List pull requests | Pull requests in a repository. | read |
| Open an issue | Opens a new issue. | messages people |
| Comment on an issue | Adds a comment to an issue or pull request. | messages people |
Opening an issue and commenting both notify everyone watching, which is why they are treated as messaging people rather than as ordinary writes.
Resend — Email
Send email from a domain you have verified with Resend. Needs a Resend API key (re_…).
| Operation | What it does | |
|---|---|---|
| Send an email | One email, from a verified sender. | messages people |
| Look up a sent email | What happened to one email that was sent. | read |
Twilio — SMS
Send a text message. Needs your Account SID and Auth Token as one line separated by a colon:
AC…:your_auth_token. Every message Twilio sends is charged to your Twilio account.
| Operation | What it does | |
|---|---|---|
| Send a text message | One SMS to one number. | messages people |
| List recent messages | What has been sent and received recently. | read |
Twilio charges per message, but an SMS has no amount for us to check, so the spending limits do not apply to it. Every send still needs a person to approve it — that approval is the only thing standing between a mistaken instruction and a phone bill.
Notion — Documents
Search and read pages and databases. Read-only: nothing here writes to your Notion. Needs an internal
integration secret (ntn_… or secret_…), shared with the pages you want it to see.
| Operation | What it does | |
|---|---|---|
| Search | Finds pages and databases the integration can see. | read |
| Read a page | One page's properties. | read |
| Read a page's content | The blocks that make up a page. | read |
| Query a database | The rows of one Notion database. | read |
Airtable — Data
Read and write rows in an Airtable base. Needs a personal access token (pat…) with access to
the base.
| Operation | What it does | |
|---|---|---|
| List rows | Rows from a table, optionally filtered by view or formula. | read |
| Look up one row | One row by its id. | read |
| Add a row | Adds one row. | needs approval |
| Change a row | Changes the columns you name, leaving the rest alone. | needs approval |
| Delete a row | Removes one row for good. | deletes data |
Google Calendar — Calendar
Read what is in a calendar and add events to it. There is nothing to paste: you sign in at Google and it hands the access to our server — see Signing in.
| Operation | What it does | |
|---|---|---|
| List calendars | The calendars this account can see. | read |
| What is coming up | Events in a calendar, soonest first. | read |
| Look up one event | One event by its id. | read |
| Add an event | Puts something in the calendar; anyone invited is emailed. | messages people |
| Remove an event | Cancels an event for good. | deletes data |
Adding one
- Select a node and open its toolbar.
- Choose the service icon. If the schema is not in the cloud yet it is moved there first — services are called from a server.
- Pick a family, search the catalogue, describe an API, or type an address yourself.
- Give it a name, then add its credential.
- Use Try it now on a read-only operation to check the connection before an agent depends on it.
Services are part of the paid plan: 25 on Pro, 125 on Pro Max, per account. They run on the schema owner's plan, and every call is metered.
Credentials
A service's key is the one thing this app holds and never keeps. When you paste one it goes straight to a server, which stores it in Google Secret Manager — outside the database, where no client can read it.
- It is never shown again, to anyone, including you and the schema owner. There is no code path from a stored key back to a screen. If you lose it, replace it.
- It is never sent to the model. It goes into the outgoing request's headers and nowhere else.
- It is redacted out of anything that comes back before the reply is recorded or shown, in case an API echoes it.
- It is checked when you paste it. Each family knows what its own key looks like, so a wrong paste is caught there and then rather than as a puzzling 401 a week later.
- Replacing one takes effect within a minute.
Clearing the box and saving removes the credential. Some families have no box at all — see Signing in.
Signing in, instead of pasting a key
Most of what people actually want a node connected to — a calendar, a mailbox, a drive — does not hand out pasteable keys at all. For those, the service's screen offers Sign in rather than a box to type into:
- Choosing it opens the provider's own sign-in page in your browser. The link is one-time and expires in ten minutes.
- You sign in at the provider — never here — and it asks whether SchemAgentic may act for you, listing exactly what it would be allowed to do.
- The provider hands the access straight to our server, over a channel your browser never sees. The app never holds it, even for an instant.
From then on it works like any other service. The access is renewed automatically before it expires; if it is ever revoked — you changed your password, or withdrew it at the provider — the service says so and asks you to sign in again, rather than failing quietly.
Sign out destroys what is held. Nothing is left behind, and the service goes back to having no credential at all.
What needs a person
An agent must have a person approve, in that run, any operation that:
| Kind | What it covers | Extra rules |
|---|---|---|
| moves money | Taking a payment, refunding one. | Also needs a spending limit, and cannot be run from Try it now at all. |
| messages people | Email, SMS, a Slack post, opening or commenting on an issue. | The agent is told to say who will receive it and what it will say. |
| deletes data | Removing a row or record. | The agent is told to say exactly what would be removed. |
| changes data | Any other write — adding or changing a row. | Marked by the family, or by whoever declared the operation. |
For a family, what an operation counts as is ours to decide, not the definition's. Editing a service document to claim that taking a payment is read-only changes nothing: the family's answer wins, every time.
An approval covers that exact operation in that run — see Approvals.
Payments
A payments service is the one that can cost you real money, so it has rules of its own on top of everything above.
Test mode and live mode
Every payments service starts in test mode, pointed at the sandbox. Its key has to match its mode: a live key in a test service is refused, and so is a test key in a live one. Switching to live asks you to confirm, because from that moment payments are real.
Limits are required, not optional
A payments service may spend nothing at all until someone sets two limits on its screen: the most it may spend at once, and the most it may spend in a month. Until both are set, every payment is refused and the agent is told so up front, so it says a limit needs setting rather than proposing a payment that would bounce.
Why it works this way round. If an unset limit meant "no limit", the most dangerous service in a schema would be the one nobody had finished setting up. So an unset limit means nothing may be spent, and spending is something a person switches on deliberately.
Amounts are typed the way you think about money — 50 or 49.99 — and both
ceilings are kept consistent: a per-payment limit is never left above the monthly one. The running total is
shown next to the monthly limit and resets at the start of each month.
What has been spent is the server's count
The total a service has spent this month is written only by the server. Nobody can edit it — not an editor, not the owner, not an agent. A limit you can raise is a decision; a counter you can reset would make the limit meaningless.
A retried run cannot charge twice
Every payment carries a key derived from the run that asked for it, the operation and the exact amount. If the same run is retried after a network failure, the payment provider recognises the same key and does not charge again. It is derived rather than random precisely so that a retry is not a new payment.
Try it now cannot spend
Try it now refuses money-moving operations outright. Checking a connection is not the same as spending, and a payment should only ever happen down the path that records who approved it.
The built-in service is the only way in
A service you describe yourself cannot be pointed at a payments API we have a family for. It would reach the same endpoints with none of what makes payments safe — no limits, no protection against paying twice, no test/live check — so that route is closed, for drafted and hand-typed services alike.
AI-drafted services
For anything not built in, describe it — "the OpenWeather current weather API" — and a definition is drafted: an address, and the operations it believes that API has, with their parameters.
A draft is a proposal, not a service. Nothing is saved until you read it and press save. That step is the point: what comes back is a guess about somebody else's API, and you are the one who can tell whether it is right. Check the operations against the API's own documentation before you rely on them.
Before you are shown a draft, the server checks it:
- The address must be https, a real public host, and not inside our network — the same check a real call makes.
- It must not be a payments API we have a family for. That is refused, with the family offered instead.
- No credential anywhere in it — the model is told not to include one, and a draft containing something that looks like a key is thrown away rather than cleaned up.
- Known methods only, at most 12 operations, and every
{placeholder}in a path has a matching parameter so no call is built with a hole in it. - Anything that is not a plain read is marked as changing data, whatever the model said. A model's "this is safe" is not accepted as an answer.
Drafting uses the model, so it counts against the owner's monthly allowance like an agent's thinking does.
Describing an API yourself
Choose Type an API address myself, give the service a name and an https:// address,
then add operations one at a time. For each: a name, a method, and a path. Use {braces} for
parts of the path that are filled in per call.
Anything that is not a GET is treated as changing data by default, so an agent has to ask before calling it. You can change that on the operation's screen — do it only for an operation you are certain is a read.
Authentication can be none, a bearer token, an API key in a header you name, or a username and password. The credential itself is stored the same way as any other, and is equally unreadable afterwards.
Receiving from outside
Everything above is the schema reaching out. A service can also be given an address the outside world posts to — Stripe saying a payment arrived, GitHub saying an issue was opened, a machine posting a reading. Choose Receive from outside on the service's screen; what arrives lands on the node's input, and its agents can be run with it.
An address anyone on the internet can post to is a different kind of thing from a call you make, so:
- It is unguessable — 32 random characters — and there is no listing of addresses. That randomness is the whole of what stands between a stranger and your node, so treat the address the way you would a password.
- It is signed by default. You get a signing secret once, when the
address is made; give it to whoever will be posting. Every request must carry an HMAC-SHA256 of the
body, in
X-Signature,X-Hub-Signature-256or Stripe's own header — all three are understood. A service set to require a signature and missing its secret refuses everything, which is the safe way round for a setting somebody half-finished. - A signature is only valid for five minutes where the sender includes a timestamp, so yesterday's genuinely-signed message cannot be replayed today.
- It is capped at 128 KB and 600 an hour, and every delivery is metered like any other service call.
- It cannot spend. A webhook delivers a value and may start the node's agents; it can never call an operation, so nothing arriving from outside moves money or messages anyone on its own.
- A disconnected schema accepts nothing — the address answers 402 and drops the body rather than queueing work nobody is paying for.
Making a new address replaces the old one, which stops working immediately. Stop receiving removes it and destroys the signing secret.
Connections held open
A webhook waits to be called. Some things do not work that way at all: an MQTT broker, a websocket feed and a server-sent event stream all push whenever they have something, down a connection that has to stay open. Those run on a separate always-on service, and a node can subscribe to one.
On the service's screen, set its transport and address:
| Transport | Address | After a reconnect |
|---|---|---|
| Server-sent events | https://… | Resumes from the last event id the server gave it, so nothing between is lost. |
| WebSocket | wss://… | Starts from now. A websocket has no standard way of saying what you missed, and we do not invent one. |
| MQTT | mqtts://… plus topics | The broker keeps your session and delivers what queued while you were away. |
An unencrypted address is refused: the credential travels on that connection. So is an address inside a private network, for the same reason a call to one is.
What arrives lands on the node's input exactly as a webhook would, and can run the node's agents. The same caps apply, plus two that only a stream needs:
- The same message twice is delivered once. Every one of these transports redelivers after a reconnect — that is what makes them reliable — so each message is remembered by its id and a repeat is dropped. Without it, one reconnect at the wrong moment would refund a customer twice.
- A connection that keeps dropping stops, with the reason on the service, rather than retrying for ever. A stream that retries for ever hides a wrong password for a month.
- 128 KB a message and 3,000 an hour; five streams per schema.
A stream costs while it is held open, not per message, so it needs a subscription the way an agent does — and when one lapses, streams are closed, never deleted. They start again by themselves when the subscription does.
Kafka and AMQP are not available yet. They are in the design, and a service asking for one is simply not a stream rather than a stream that connects to nothing.
Watching for a change
Where an API has neither a webhook nor a stream, a service can poll it — call a read-only operation on a schedule and deliver only what is new.
- The first check delivers nothing. It records where things stand. Otherwise turning polling on would flood the node with the entire history of whatever it is watching.
- What counts as new is either an id it has not seen, a timestamp later than the newest one so far, or a change in the whole answer — whichever suits what you are watching.
- Only read-only operations can be polled. Something that writes is not a thing to do on a timer with nobody watching, and there is no one to approve it at three in the morning.
- Fifteen minutes is the shortest interval.
Every rule a call passes
In order, every time, for every call — whether it came from an agent or from Try it now:
- The operation must exist on that service. For a family, the operations are the family's, so a document cannot invent one.
- The address is the family's, where there is one. It is not a field anyone can point elsewhere.
- The URL must be https, with no credentials in it.
- The host is resolved and every address it resolves to is checked. Anything private —
10.x,127.x,192.168.x,169.254.xand the rest — is refused. This is checked again on every redirect, because a public host is free to redirect to a private one. At most three redirects. - Money operations: limits must be set, the amount must be within both of them, and the key must match test or live.
- Anything sensitive or data-changing must have been approved, for that exact operation, in that run.
- The credential is added to the request's headers, and only there.
- The call is given 10 seconds and at most 64 KB of reply.
- The reply is redacted of anything matching the credential before it is recorded or shown.
- The call is metered, and the service's health and last error are updated.
Only GET, POST, PUT, PATCH and DELETE are allowed. An agent may make at most 8 service calls in one run.
Who can change what
| Field | Owner / editor | Viewer | Server |
|---|---|---|---|
| Name, operations, address, limits, test/live | Yes | No | — |
| The credential | Write only, never read | No | Reads it for a call |
| Health, last error, call count | No | No | Yes |
| What has been spent this month | No | No | Yes |
When something goes wrong
| What you see | What it means |
|---|---|
| "This service has no key yet" | Add its credential on the service's screen. |
| "That does not look like a … token" | The key is for the wrong service, or truncated. Each family knows the shape of its own. |
| "This service is set to test, but that is a LIVE key" | Either switch the service to live, or paste a test key. The two must agree. |
| "Nobody has said what this service may spend" | Set both spending limits on the service's screen. |
| "That is more than this service is allowed to move in one go" | The amount exceeds the per-payment limit. Raise the limit deliberately, or have the agent propose less. |
| "That would put this service over what it may move this month" | The monthly ceiling would be crossed. It resets at the start of the month. |
| "This operation moves money. It can only be run by an agent, with a person's approval." | Try it now will not spend. Use a read-only operation to check the connection. |
| "That address is not reachable from a service" | The address resolves to a private or local network. Services reach public APIs only. |
| "Services must use https" | Plain http is never allowed. |
| "This service points at … which has a built-in service of its own" | Use the built-in family; it carries protections a hand-described service would not. |
| A warning or error dot on the node | A call failed. Open the service to see the last error. |
| "Services need a Pro plan" | The schema owner is on the free plan, or their subscription lapsed. |
| A sender reports 401 from your address | The signature did not match. Check they are using the secret you were shown, and signing the exact body they send. |
| A sender reports 402 | The schema is disconnected — the owner's subscription has lapsed. |
| A sender reports 429 | More than 600 in an hour reached that address. |