MacroLinker docs

How to connect your company’s outside tools once, hand each app a token, and let those apps use the tools without ever holding the keys.

On this page

MacroLinker is where your company connects the outside services it works with: the CRM, email, chat, files, code and the support desk. You connect each service once. Then the apps you trust, such as MacroCaptain or your own code, can use that service through MacroLinker without ever holding its password or key.

Each app gets its own consumer token. When the app needs something from a service, it sends the request to MacroLinker with that token. MacroLinker checks the app may use the connection, makes the call with your credential, retries if the service has a hiccup, paces calls so no service is flooded, and writes the call to a log. When the call comes from MacroCaptain, MacroLinker also reports what it cost.

MacroCaptain (formerly MacroAgentic) plans and runs work across the Macro agents. There are two kinds of connection, and they live in different places.

Macro agents connect in MacroCaptain

MacroMkt, MacroPrimer, MacroReply, MacroSession, MacroDeploy and the other Macro agents are connected in MacroCaptain, with one key per company. When a step is approved, MacroAssembly fetches that key and calls the agent directly. MacroLinker is not in that path.

Outside tools connect in MacroLinker

HubSpot, Gmail, Slack, LinkedIn, Instagram and the other services below are connected here. MacroCaptain holds a MacroLinker consumer token and sends every outside call through it, so the call uses your connection, your rules and your log.

Some agents need both. MacroPipe, for example, is a Macro agent, so its key lives in MacroCaptain. It also works in your CRM, so it needs HubSpot connected in MacroLinker.

When an agent in a MacroCaptain run needs an outside service, it asks MacroCaptain. MacroCaptain decides whether the step may do it and how much it may spend, then sends the call to MacroLinker with a short-lived grant. MacroLinker makes the call and reports the cost back. Today MacroCaptain keeps the MacroLinker token per person: each person pastes their own token in MacroCaptain’s Settings, and a run uses its owner’s token.

  1. 1

    Sign in

    Go to macrolinker.com/login and continue with Google, or ask for a sign-in link by email. Your first sign-in creates an organization named after you, with one workspace called Default. You are its owner.
  2. 2

    Connect a service

    Open Connections and pick a service. Leave it set to Company and All workspaces unless you need otherwise, then choose Connect with a credential and paste the key the card asks for. The card links to the service’s own instructions for creating one.
  3. 3

    Create a consumer token

    Open Consumers, name the app that will use it, and say who uses it: MacroCaptain or your own code (see Consumers). Choose Issue token and copy it right away. It starts with ml_ and is shown only once.
  4. 4

    Use the token

    For MacroCaptain, open MacroCaptain, go to Settings and paste it into MacroLinker consumer token. For your own code, send it as a bearer token to the execute API.

A connection is one service, connected once, with its credential stored encrypted in MacroLinker. You make two choices when you connect.

Company or personal

A company connection belongs to the organization. It keeps working when the person who added it leaves, and it is the only kind an app can use. Only owners and admins can add or remove one. A personal connection belongs to you. Only you can see it, it is never handed to an app, and it leaves with you.

All workspaces or one

An organization can have several workspaces, for example one per team or client. A connection made for All workspaces is shared by every workspace. A connection made for one workspace is private to it, and it wins over the shared one for the same service. Owners and admins create workspaces from the workspace menu at the top of the console.

Which apps may use it

By default every consumer token in the workspace may use a connection. On a connected card you can narrow that to a chosen list of consumers. To cut off access entirely, disconnect the service.

When a connection expires

A connection made by signing in with the service carries a key that expires. When an app uses it close to that time, MacroLinker renews it first. If renewing fails, the connection is marked as needing attention, everyone in the organization gets an alert, and calls fail until someone reconnects it. The Overview page shows how many connections need attention.

These are the services MacroLinker can connect today. Every one can be connected by pasting a credential. Signing in with the service is offered on a card only where it has been switched on for MacroLinker; it is not switched on for any service yet, so today you connect by pasting.

ServiceWhat you pasteExecute API base address
Google DriveService account JSONhttps://www.googleapis.com/Signed-in connections only
SlackBot tokenhttps://slack.com/api/
GitHubPersonal access tokenhttps://api.github.com/
IntercomAccess tokenhttps://api.intercom.io/
ZoomAccount ID, Client ID, Client secrethttps://api.zoom.us/v2/Signed-in connections only
VercelAccess token, Team ID (optional)https://api.vercel.com/
SnowflakeAccount identifier, User, Password or PAT, Warehouse (optional), Role (optional)https://{account}.snowflakecomputing.com/api/v2/
SalesforceAccess token, Instance URLhttps://your-instance.my.salesforce.com/services/data/
ZendeskSubdomain, Agent email, API tokenhttps://{subdomain}.zendesk.com/api/v2/
AtlassianSite URL, Account email, API tokenhttps://api.atlassian.com/
DropboxAccess tokenhttps://api.dropboxapi.com/
HubSpotPrivate app tokenhttps://api.hubapi.com/
GmailAccess tokenhttps://gmail.googleapis.com/
LinkedInAccess tokenhttps://api.linkedin.com/
InstagramAccess token, Instagram user IDhttps://graph.instagram.com/

The base address is where the execute API sends a call. A part in braces, such as {subdomain}, is filled in from what you pasted.

For Google Drive and Zoom, what you paste is a key file or app credentials, not a token the execute API can send. The execute API works with these once they are connected by signing in with the service.

For Zendesk, the execute API needs the pasted credential, because the address is built from a field you paste.

A consumer is an app that may use your connections: MacroCaptain, a script, a service you run. Give each one its own token, so you can cut one off without touching the others.

Used by MacroCaptain, or by your own code

When you issue a token you say who will use it. A token for MacroCaptain only works with grants MacroCaptain signed, with a key that only MacroCaptain and MacroLinker hold. If the token leaks, it reaches nothing on its own. A token for your own code works with grants your code writes itself, so keep it as safe as the services it can reach. Tokens issued before 1 November 2026 count as your own code; to get the protection, issue a new MacroCaptain token and revoke the old one.

The token

A token looks like ml_ followed by 32 random characters. It is shown once, when you issue it. MacroLinker keeps only a fingerprint of it and the last four characters, which the Consumers page shows so you can tell tokens apart. If you lose a token, issue a new one.

Scope

Each token belongs to the workspace that was open when it was issued. It can reach that workspace’s connections and the ones shared with all workspaces, and nothing else: not another workspace, not another organization, and never anyone’s personal connection. The workspace comes from the token itself, so a caller cannot ask for a different one.

Revoking

On the Consumers page, choose Revoke next to the token. It stops working on the next request. Revoked tokens stay listed, marked as revoked, so the history still makes sense. Only owners and admins can issue or revoke tokens, and everyone else in the organization gets an alert when they do.

The execute API makes one call to a connected service on your behalf. The credential never leaves MacroLinker: you name the service and the request, and MacroLinker adds the key.

Request
curl -X POST https://macrolinker.com/api/v1/execute \
  -H "authorization: Bearer ml_…" \
  -H "content-type: application/json" \
  -d '{
  "grant": {
    "grantId": "grant_8f2c",
    "runId": "run_41",
    "stepId": "step_3",
    "agentId": "macropipe",
    "provider": "hubspot",
    "issuedAt": "2026-11-01T15:00:00.000Z",
    "expiresAt": "2026-11-01T15:05:00.000Z",
    "ceilingCents": 500
  },
  "call": {
    "operation": "http",
    "params": {
      "method": "GET",
      "path": "crm/v3/objects/contacts",
      "query": {
        "limit": "10"
      }
    }
  }
}'
Response
{
  "ok": true,
  "result": {
    "status": 200,
    "body": { "results": [ … ] }
  },
  "costCents": 0,
  "retries": 0,
  "rateLimited": false,
  "callId": "cm3x9…"
}

The grant

The execute API was built for MacroCaptain, which writes and signs a grant for every approved step. If you call it from your own code with a token for your own code, you write the grant yourself and leave issuer and sig out. The ids are your own labels: they are stored with the call and sent with its cost report.

FieldMeaning
providerThe service to call, by its id: hubspot, slack, gmail and so on (see the list below).
grantId, runId, stepId, agentIdLabels for the grant, the run, the step and the agent making the call. Each must be non-empty.
issuedAt, expiresAtTimes as ISO 8601 strings. The call is refused once expiresAt has passed.
ceilingCentsWhat this grant may spend, in cents. Every call made on the grant counts against it, and a call that would go past it is refused.
operationOptional. A label for what the grant allows.
issuer, sigSet by MacroCaptain: who signed the grant and the signature. Required for a MacroCaptain token, which refuses a grant whose fields were changed after signing.

The call

FieldMeaning
operationAlways http. Anything else fails without calling the service.
params.methodGET, POST, PUT, PATCH or DELETE. Defaults to GET.
params.pathThe path, relative to the service's base address. Required.
params.queryOptional. Query string values, all strings.
params.headersOptional. Extra headers. MacroLinker always sets the authorization header itself.
params.bodyOptional. Any JSON value. It is sent as JSON.

Write the path without a leading slash, so it is added to the base address. With Slack’s base https://slack.com/api/, the path chat.postMessage reaches https://slack.com/api/chat.postMessage. A leading slash starts from the root of the host instead. A path that would leave the service’s host is refused.

Service ids: google_drive, slack, github, intercom, zoom, vercel, snowflake, salesforce, zendesk, atlassian, dropbox, hubspot, gmail, linkedin, instagram.

The response

Once MacroLinker has tried the call, it answers with HTTP 200, even when the service said no. Read ok to know whether the service accepted it.

FieldMeaning
okTrue when the service answered with a 2xx status.
result.statusThe service's HTTP status, or 0 if no answer came back.
result.bodyThe service's answer: parsed JSON when it is JSON, otherwise the raw text.
errorPresent when ok is false: HTTP 404 and the like, or the reason the call could not be made.
costCentsWhat the call cost, in cents. See cost reporting.
retriesHow many times MacroLinker retried before this answer.
rateLimitedTrue if the service asked MacroLinker to slow down at any point during the call.
callIdThe id of the call in MacroLinker's log.

These answers come back before the service is called. Nothing is sent to the service, nothing is logged as a call, and nothing is charged. Apart from the first four, the body also carries "ok": false, "costCents": 0 and "retries": 0.

StatusErrorWhat it means
401missing bearer tokenNo token was sent.
403invalid tokenThe token is not one MacroLinker issued.
403token revokedSomeone revoked this token. Issue a new one.
400A list of problemsThe body does not match the shape above.
403grant expiredexpiresAt is in the past.
403grant has no remaining budgetceilingCents is zero or less.
403this consumer only accepts grants signed by macrocaptainA MacroCaptain token was sent with a grant MacroCaptain didn't sign, or one changed after signing.
402this call would take the grant past its … budgetThe calls already made on this grant plus this one would cost more than ceilingCents.
503grant signatures can’t be checked right nowMacroLinker is missing the key it checks MacroCaptain’s grants with. Try again later.
429rate limit for …MacroLinker’s own pacing. Wait for the retry-after header (one second) and try again. The body has "rateLimited": true.
404… is not connectedNo company connection for this service in the token’s workspace, or an unknown service id. code is not_found.
403this consumer is not allowed …The connection is limited to other consumers. code is forbidden.
409The renewal errorThe connection expired and could not be renewed. Someone must reconnect it. code is expired.
422no API base known for …The execute API cannot reach this service yet, or the connection lacks the field the address is built from.
409connection for … holds no usable credentialThe pasted credential is not a token MacroLinker can send. Connect by signing in with the service instead.

Retries

If the service times out, cannot be reached, answers 408 or 429, or answers with a 5xx error, MacroLinker tries again, up to 3 more times. It waits 0.5 s, then 2 s, then 5 s between tries, or as long as the service’s retry-after header asks. Each try may take up to 60 s. Any other 4xx answer comes straight back, because trying again would not change it.

All of this happens inside your one request, so a slow service can keep the request open for a few minutes. Set your client’s timeout with that in mind.

Retries apply to every method, including POST. If a service does the work and then fails to answer, a retry can do it a second time. Calls that create things are best made with the service’s own duplicate protection, where it has one.

Rate limits

MacroLinker paces calls to each service: up to 10 at once, then 5 a second. This is a guard against bursts, shared by everyone calling that service, not a quota you buy. A call over the limit gets a 429 straight away and is not sent. Services also have their own limits. When a service answers 429, MacroLinker waits and retries as above, and the response says "rateLimited": true.

One more endpoint takes the same consumer token. It lists what the token may use: company connections that are working and that this consumer is allowed to use, one per service, and never a credential.

What can this token use?
curl https://macrolinker.com/api/v1/connections \
  -H "authorization: Bearer ml_…"

{
  "org": { "id": "…", "slug": "…", "name": "Acme" },
  "workspace": { "id": "…" },
  "consumer": { "id": "…", "name": "MacroCaptain" },
  "connections": [
    {
      "provider": "hubspot",
      "name": "HubSpot",
      "authType": "credential",
      "capabilities": ["Read contacts & deals", "Update records", "Enroll in sequences"],
      "accountLabel": null,
      "scopes": [],
      "connectedAt": "2026-10-30T18:22:41.000Z"
    }
  ]
}

MacroLinker never hands a credential to a consumer. An older endpoint that did, POST /api/v1/connections/<service>/token, was retired on 1 November 2026 and now answers 410. Send the call through the execute API instead, so it is paced, logged and costed.

What is checked on every call

Before the execute API calls a service, MacroLinker checks, in order: that the token is valid and not revoked; that the grant has not expired; for a MacroCaptain token, that MacroCaptain signed the grant and nothing in it changed since; that the calls already made on the grant plus this one fit its budget; that the service is within its pace; that the token’s workspace has a company connection for the service; and that this consumer is on the connection’s list, if it has one. Finally, the path must stay on the service’s own host. The larger decisions, such as which agent may use which service and how much a run may spend, are made in MacroCaptain before the call is sent.

The audit trail

MacroLinker keeps two records. Every time a credential is used, whether by the execute API or handed out by the credential API, it records which connection, which consumer and when. It never records the credential. The Overview page shows the latest of these as Recent access.

Every call the execute API tries is also logged: the consumer, the service, the grant, run, step and agent ids, the method and path, the service’s status, whether it succeeded, how many retries it took and what it cost. Query strings, headers and the bodies sent and received are not stored. This call log is not shown in the console yet.

Each call through the execute API has a cost, set per service. Today it is zero for every service except Snowflake, which counts 1 cent a call. Calls refused before they reach the service cost nothing.

After each call, MacroLinker reports the cost to MacroCaptain with the call id, the service and the run, step and agent it belongs to, so MacroCaptain can book it against the run. MacroCaptain counts each call id once, so a repeated report is never charged twice. If the report cannot be delivered, MacroLinker notes the failure and keeps its own record of the call; it does not hold up your answer.

The bell at the top of the console shows alerts for the workspace you have open. Everyone in the organization gets them. MacroLinker raises one when a connection expires and cannot be renewed; when someone issues or revokes a consumer token (the person who did it is not alerted); when 5 or more calls to one service fail within 15 minutes; when calls to a service are being slowed down; and when a workspace’s call costs run unusually high within an hour. Repeat alerts about failures, slowdowns and costs are sent at most once an hour, so a bad afternoon does not bury you.

You sign in to MacroLinker with Google or with a sign-in link sent to your email. Your account is your verified email address. If you first signed in with Google, a sign-in link to the same address opens the same account. The other way round does not work today: if you started with an emailed link, keep using emailed links.

Each Macro product has its own account. Across Macro products, accounts are treated as the same person only when they use the same verified email address. Nothing is matched by name or company. MacroLinker and MacroCaptain are tied together by the consumer token you paste, not by your sign-in.

If you use a different email in another Macro product, create an account there with the same email you use here, then contact support to ask whether your data can be moved.

Signing out, from the Account page, ends your session in that browser only. Your connections and tokens keep working, so apps are not interrupted.

MacroLinker is in early access. It has no paid plans yet and nothing in it charges you. The per-call cost above is reported to MacroCaptain for its budgets and records; it is not a MacroLinker bill.

When paid plans start, a MacroLinker account will be paid either directly or through MacroCaptain (see Coming next). Links between Macro products, such as the token MacroCaptain uses to reach MacroLinker, are free on every plan.

Credentials are encrypted before they are stored, with AES-256-GCM and a key kept apart from the database. They are decrypted only at the moment a call is made or a credential is handed out. They never reach the browser: the console shows a masked hint, such as xox…9f2c, so you can tell which key is connected. When you paste a credential, only the fields that service asks for are kept.

Consumer tokens are stored only as a SHA-256 fingerprint, so not even MacroLinker can show one again. A token is tied to one workspace in one organization, and that is read from the token, never from the request. Personal connections are never handed to an app. Answers that carry a credential are marked so they are never cached.

The execute API sends calls only to the service’s own host, and it always sets the authorization header itself, so a caller cannot redirect a key elsewhere. What is logged is listed under the audit trail: who used which connection and when, and the outline of each call. Credentials, headers and the content of requests and answers are not logged.

Not live yet

These are planned. None of them works today.

  • One-click Connect from MacroCaptain

    Link MacroCaptain to MacroLinker with one click, so nobody has to copy and paste a token.

  • One token per company

    The MacroLinker token in MacroCaptain will belong to the company or workspace instead of to each person.

  • Billing through MacroCaptain

    Accounts that MacroCaptain creates will be billed through MacroCaptain at each product's list price, with package discounts for using several products.