Integrations
Connect the accounts you have: your agents post, run ads, send and book as you, and every reply, click and payment returns as one record.
- Integrations
- 25
- Ways a call runs
- One reads, two act
- Account calls
- 4
Connect my Google Search Console with read-only access, and show me what my agents can then read.
curl -X POST "https: //api.sb/ leaseline.app/ integrations/ connect? mode= try" \
-H "Authorization: Bearer $SB_TOKEN" \
-H "Content-Type: application/ json" \
-d '{"system": "seo", "scope": "startup", "grants": ["webmasters.readonly"], "for": "dana@leaseline.app"}'
npx startup-builder do Integration.connect --try --data '{"system": "seo", "scope": "startup", "grants": ["webmasters.readonly"], "for": "dana@leaseline.app"}'
{
"name": "do",
"arguments": {
"mode": "try",
"code": "return await $.Integration.connect({ system: 'seo', scope: 'startup', grants: ['webmasters.readonly'], for: 'dana@leaseline.app' })"
}
}
import { $ } from 'startup-builder'
const result = await $.try.Integration.connect({ system: 'seo', scope: 'startup', grants: ['webmasters.readonly'], for: 'dana@leaseline.app' })
{
"$type": "Integration",
"mode": "try",
"stored": false,
"state": "connecting",
"system": "seo",
"grants": [
"webmasters.readonly"
],
"connectUrl": "https: //api.sb/ leaseline.app/ integrations/ int_Gq7sc/ approve",
"connectUrlExpiresAt": "2026-10-27T15: 42: 00Z",
"unlocks": [
"Domain.verify",
"Metric.query"
]
}
The overview, in a founder's words: Headless SaaS: every write is one record and one Event.
Three ways a call runs: one reads the market, two act as you
Reading the market needs no account. A Data call reads public data about anyone, such as a rival’s ads, a results page or the people who commented on a post, and returns a reading. It never acts, so it never needs your sign-in.
Acting needs an account: posting, running ads, sending, booking and reading your own analytics. Your agent acts as you on every site you authorize. Where a site has no API, a browser agent signs in as you and does the steps.
You never pick the way. Each Verb carries ranked Mappings, and the platform runs the first one your accounts allow. Post.publish is one call on LinkedIn and on Reddit: LinkedIn takes it through your account’s API, and Reddit through a browser agent signed in as you. Every Action records which way it ran.
One standing approval covers both ways of acting, because a standing approval names a Verb, its topics and its Limit, never an endpoint. A spend cap set with Mandate.authorize on Campaign.update holds on Meta Ads and on LinkedIn Ads alike, both through your ad account’s API.
| Way | It | What you connect | What it costs |
|---|---|---|---|
| Data | Reads the market: public posts, ads, profiles, rankings, answers and comments about anyone | Nothing | Priced per result before it runs, usually a fraction of a cent; see the Data API |
| Your account | Acts through the site’s API on an account you approved: post, run ads, read your analytics, send from your inbox | One sign-in per account, with the narrowest grants each job needs | Free on the platform; the site bills your ad spend directly, and X bills each post |
| Browser agent | Acts in the site’s own screens where it has no API for the step, signed in as you in a saved session | One sign-in, kept in your saved browser session | About 1.6¢ a step, settled on steps used |
Connect once per account, grant per job
Integration.connect returns a connectUrl on the site’s own sign-in page, good for one hour. You approve there, and the Integration moves from connecting to connected with the grants you approved and no others.
One sign-in with Google can grant Search Console, Analytics, Business Profile, Ads, YouTube, Gmail and Calendar, one grant at a time, as each Play first needs it. One sign-in with Meta covers your Facebook page and your ad account. Each grant is its own Integration, so pausing Ads never pauses Gmail.
state is read fresh before every call: connecting, connected, needsReauth, paused or revoked. The platform renews each token before it lapses and asks you to reconnect 7 days before a sign-in runs out; a call that meets needsReauth refuses with needs-reauth and runs once you reconnect.
import { $ } from 'startup-builder'
const gmail = await $.Integration.connect({ system: 'mailboxes', venue: 'gmail', scope: 'startup', grants: ['send', 'read-replies'] })
// open gmail.connectUrl, approve, and the Integration is connected | To | Call | What happens |
|---|---|---|
| Connect | Integration.connect | Returns the connectUrl; the Integration stays connecting while the sign-in page waits |
| Pause | Integration.pause | Stops every call through the account at once and keeps the token and cursor |
| Resume | Integration.resume | Restarts from the cursor and releases the Actions that waited |
| Reconnect | Integration.reconnect | A fresh connectUrl; the id and cursor stay, so nothing imports twice |
| Disconnect | Integration.revoke | Revokes the token with the site; every record it brought in stays |
The integrations, by the stage you meet them at
Each account sits once, under the first of the six stages a founder with no customers meets it at: Get found, Start conversations, Make the sale, Keep customers, Grow each customer and Earn referrals. Each page holds the grants, the calls your agents make, what the account holds, its webhooks, its limits and what try does there.
Get found
| Account | Sites | Runs through | Its calls |
|---|---|---|---|
| Bing Webmaster | Bing | Your account or a browser agent | Domain.connect, Content.publish, Metric.query |
| Google Analytics | Your records | Your account | Metric.query, Audience.create, Integration.call |
| Google Business Profile | Google Business Profile | Your account | Conversation.answer, Post.publish, Metric.query, Integration.call |
| Google Search Console | Your records | Your account | Domain.verify, Metric.query, Domain.connect, Content.publish, Monitor.create |
| Hacker News | Hacker News | A browser agent | Post.publish, Conversation.answer |
| Product Hunt | Product Hunt | A browser agent | Post.schedule, Conversation.answer |
| A browser agent | Post.publish, Conversation.answer | ||
| YouTube | YouTube | Your account | Post.publish, Post.schedule, Conversation.answer, Post.withdraw, Metric.query |
Start conversations
Make the sale
| Account | Sites | Runs through | Its calls |
|---|---|---|---|
| Google Calendar | Your records | Your account | Meeting.book, Meeting.update |
| Stripe | Your records | Your account | Offer.publish, Order.pay, Subscription.resize, Order.refund, Transaction.list |
| The CRM you already use | Your records | Your account | Integration.sync, Integration.connect, Lead.qualify, Deal.advance |
Keep customers
Nothing to connect: these Plays run on your own records, through the Systems under the App API.
Grow each customer
Nothing to connect: these Plays run on your own records, through the Systems under the App API.
Earn referrals
Nothing to connect: these Plays run on your own records, through the Systems under the App API.
Connect three accounts first, about 10 minutes
Each strategy names the accounts its first week runs on. Connect those before anything else; the rest connect when a Play first needs them, and each Play page says which.
| Strategy | Connect first | Then, as its Plays start |
|---|---|---|
| Hands-on | Gmail or Outlook, Google Calendar, LinkedIn | Reddit, X, the CRM you already use, Stripe for the paid pilot |
| Paid | Meta Ads, Google Ads, Google Analytics | Microsoft Ads, LinkedIn Ads, Reddit Ads, X Ads, TikTok Ads, Stripe |
| Launch and search | Google Search Console and Bing Webmaster, both through your Domain, and Google Analytics | Product Hunt, Hacker News, X, LinkedIn, YouTube, Google Business Profile |
Six refusals cover every call through an account
Every call through a connected account can meet these, whatever the site. Each answers with the hub problem and names the call that clears it in next, so your agent recovers without asking you.
| Code | Status | Refuses when | Fix | Then call |
|---|---|---|---|---|
grant-missing | 403 | the account is connected without the grant this call needs, such as reading replies on an inbox that only sends | Connect the grant the error names with POST /{namespace}/integrations/connect; the grants you already gave stay as they are. | Integration.connect |
needs-reauth | 409 | the site refused to renew the sign-in, so calls through the account wait | Approve again at the connectUrl POST /{namespace}/integrations/{integration}/reconnect returns; the waiting calls then run, oldest first. | Integration.reconnect |
account-not-connected | 409 | the call needs an account you have not connected, such as your Search Console property or your Meta ad account | Connect the account with POST /{namespace}/integrations/connect, approve it at the connectUrl, then send the call again. | Integration.connect |
over-mandate | 403 | the call would pass your standing approval’s spend limit, daily count, topics or Channels | Raise the limit with POST /{namespace}/mandates/authorize, or wait for its window to reset; the call stays held. | Mandate.authorize |
quota-reached | 429 | the account’s own daily or per-minute cap is used up, such as 2,000 page checks a day per Search Console property | Retry after the reset time the response gives; nothing was sent or spent. | Action.retry |
venue-rejected | 422 | the site rejected the ad or removed the post, and its reason passes through in detail | Change what the site’s reason names, then send the same call again. | Ad.update |
An endpoint no call covers
Integration.call sends one request to the site through your Integration, with the token filled in on the server, for any endpoint inside your grants that no Verb maps. In try mode it runs the site’s own validation where the site has one, and changes nothing.
const res = await $.try.Integration.call({ integration: 'integrations/google-analytics' , method: 'POST', path: '/v1beta/properties/412339871/keyEvents' , body: { eventName: 'sign_up' } })The reference for the calls these accounts make is the App API; the overview is Headless SaaS, and how your agents act as you is on Agents.