Connections
Let your app read and write a person's Google or Microsoft account without ever holding their password or token.
A connection lets your app use someone's Google or Microsoft account. The person agrees once, on a consent screen. After that, the platform makes each call for your app and adds the login itself. Your app never sees a password, a token or a secret.
Think of a hotel concierge. You ask for a taxi. The concierge makes the call from the desk phone, using the hotel's account. You never hold the phone line. If the hotel stops the account, your next request is simply refused. A connection works the same way: your app asks, the platform calls Google or Microsoft and brings back the answer.
Declare one
A connection starts as a few lines in cohostly.json:
{ "manifest_version": 2, "bindings": { "connections": { "agenda": { "provider": "google-workspace", "permissions": ["calendar.read"], "access": "user" } } } }
Each connection has a name (agenda here) and these fields:
providerrequiredstringgoogle-workspace or microsoft-graph.permissionsrequiredlist of stringsaccessowner or userowner. See owner and visitor mode.require_approval_for_writestrue or falseThe name is part of your code. It becomes platform.connections.agenda, the address /connections/agenda/..., and the setting CONNECTION_AGENDA_URL. Renaming it later breaks the app. A name uses lower-case letters, digits and underscores, up to 32 characters. The manifest schema keeps ai, approvals, data and connections for the platform, so pick something else.
After you change the file, run cohostly types again so the generated platform.gen.ts knows your connection (build and test locally).
Good to know A deploy never fails because of a connection. If something looks wrong, you get a warning instead. See what a deploy tells you below.
Provider ids
The provider value names a connector. These are the ones the platform offers:
google-workspaceGooglegoogle-sheetsGooglesheets.files, sheets.read, sheets.write. Kept so existing apps keep working.google-driveGoogledrive.files (the Drive files a person picks). Kept so existing apps keep working.microsoft-graphMicrosoftslackSlackAll three Google ids use the same Google connection and the same consent. sheets.write means exactly the same thing in google-sheets and in google-workspace.
Careful Ask only for permissions that belong to the provider id you named. A call can use only the permissions of the connector named in
provider. Withprovider: "google-sheets"andpermissions: ["calendar.read"], the person can be asked and can say yes, but every call is refused withpermission_not_granted. If you want Calendar, usegoogle-workspace.
If you name a provider the platform does not offer, calls answer unknown_provider and the deploy warns you.
Three locks, all must open
A call goes through only when three separate things agree:
- The manifest says your app asked for this permission.
- The grant says a person gave this permission to this tool. They can give less than you asked for.
- The login says the provider itself allowed it, because the person approved it on the provider's consent screen.
The permissions that pass all three are called effective. The platform checks again on every call, so taking something away works at once. If a call is refused, the error catalogue says which lock held.
Make a first call
Once someone has connected, a call names the provider's own path:
const rows = await platform.connections.sheet.get({ path: `v4/spreadsheets/${id}/values/A1:C100`, })
import os, requests base = os.environ["CONNECTION_SHEET_URL"] headers = {"x-platform-binding-token": os.environ["PLATFORM_TOKEN"]} reply = requests.get(f"{base}/v4/spreadsheets/{sheet_id}/values/A1:C100", headers=headers, timeout=35) rows = reply.json()
The better way, for Google and Microsoft, is to hand the official library to the connection. Every function and type is the provider's own. Calling the API covers both ways in full.
Who sets it up
A connection belongs to an account (the workspace that owns the project), not to one person.
- In
ownermode, an account owner or admin connects the Google or Microsoft account in the dashboard, under Connections. A project admin of the same account gives it to the tool. Until both have happened, calls answerno_grant. - In
usermode, nobody sets anything up in advance. Each visitor connects their own account the first time, on a page the platform provides. The visitor must be signed in to Cohostly, because the connection is kept under their Cohostly sign-in.
The step-by-step flows are in connect, reconnect, switch and disconnect. An AI assistant working for you can list and describe connections and give one to a tool. It cannot connect or disconnect an account, because agreeing to that is always a person's act.
What a deploy tells you
A deploy checks your connections and prints warnings. It never refuses to deploy because of them.
- A provider the platform does not offer. The warning names the provider and lists the ones offered.
- A permission the provider does not have. The warning lists the permission names that are offered.
- An
ownerconnection nobody has given to the tool yet. The warning says to open Connections and connect or give one, and that until then calls answer403 no_grant. - An
access: "user"connection. It is skipped. Each person connects on first use.
How your keys stay safe
The app holds only PLATFORM_TOKEN, a pass that works for this project's bindings, and, in user mode, the visitor's signed request context. The login for Google or Microsoft is stored encrypted, with a key your app cannot reach. When a call arrives, the platform decrypts the login for that one call, sends the request to the provider, and returns only the provider's answer.
The platform does not hand the app a real token on purpose. A token inside the app would be copied into snapshots and would make file limits, write approval and instant revoke impossible. Because the platform stays in the middle, it can do all three.
Logs record who called what (connection, project, method, status, time). They never record request or response bodies.
Slack
Slack is not set up yet. A Slack connector is listed with four permissions, but do not build on it. The SDK has no Slack helper either, only the plain get and post calls.
chat.writewrite onlychannels.readreadchannels.historyreadusers.readreadIn this section
allowed(), its four states and connect_url.Holding writes for approvalthe 202 answer and how to finish the write.Errorsevery code, its exact message and the fix.Limitsrate, size and time limits.Worked examplesa sign-up sheet, a personal agenda and a multi-visitor connect page.Related: approvals, environment, request context.