Using API v1? See what changes in v2, or the v1 docs
Connecting accounts
Before you can read or write a user's calendar, the user connects their Google, Microsoft Outlook or Zoom account to your app. You can set this up in two ways. You can add PyasConnect, a ready-made sign-in dialog, to your page, or you can build your own screens on the API.
| PyasConnect | Your own screens | |
|---|---|---|
| What you build | One endpoint on your server, and the component on your page | The provider buttons, the redirects, and two requests from your server |
| Requests from your server | One, from your endpoint | Two, one to get a sign-in URL and one to send the code |
| Works with | Pyas Auth and Native Auth | Pyas Auth and Native Auth |
With either option, your API key stays on your server, and only your server uses it. To connect real accounts, you need a live key. A test key works only in the sandbox, which already has its own accounts.
Pyas Auth or Native Auth
For each provider, you choose how people sign in. You set this in the dashboard, under your app's Provider setup.
- Pyas Auth is the default. People sign in through Pyas's own OAuth app for Google, Microsoft or Zoom, so you don't need to set anything up. The provider's consent screen shows the Pyas name.
- Native Auth uses your own OAuth app for Google, Microsoft or Zoom. The provider's consent screen shows your app's name. To use it, enter your OAuth app's client ID, its client secret and your callback URL under Provider setup.
Additional scopes
By default, a connection asks for the access most apps need: reading and writing events, reading calendars, free/busy and settings. Some features need more access, so the user is asked for it only when your app requests the feature. There are two ways to request them:
- In the dashboard, under your app's Provider setup, in Additional scopes. Choose them once for each provider, and every connection from your app asks for them, in PyasConnect and on your own screens.
- In a request, with
featureswhen you create a connect session or a sign-in URL. A request that includesfeaturesuses those instead of the dashboard's choice.
| Feature | What it adds |
|---|---|
manageCalendars | Create, rename, recolor and delete calendars, and add calendars to the person's list or remove them. |
sharing | Share a calendar with other people and change their access. |
meetingSettings | Change Google Meet and Microsoft Teams settings, such as who can join without waiting. |
attachmentUploads | Upload files to events. |
rooms | List meeting rooms and book them for events. |
On Google, manageCalendars, sharing, meetingSettings, rooms and attachmentUploads need Native Auth for now. Google must approve this access for Pyas's own Google app first. If your app uses Pyas Auth for Google and requests one of them in features, the request fails with CONNECTION_PROVIDER_NOT_CONFIGURED. In the dashboard, they can't be chosen until then. With Native Auth, add each feature's scopes to your own Google app; the dashboard lists them, and any Google API to turn on.
On Microsoft, every feature works with either option. For rooms, the user's Microsoft 365 administrator must approve the access, so people whose organization hasn't approved it can't connect while you request it.
Option 1: PyasConnect
PyasConnect is a sign-in dialog that you add to your page. It shows the provider buttons, handles the sign-in, blocked pop-ups and errors, and saves the connected account. Before you start, add your site to your app's allowed origins in the dashboard. PyasConnect works only on those origins.
To try it first, open Lab in the dashboard. Pick your app, change how PyasConnect looks and behaves, and connect a real account in the preview, with no server code. The Lab then gives you the code for Vue, React, Angular or plain HTML, already filled in for your app.
1. Add an endpoint to your server
PyasConnect calls this endpoint each time a user opens the dialog. With the SDK, the endpoint is one line of code:
import { Pyas } from '@pyas/sdk'
const pyas = new Pyas({ apiKey: process.env.PYAS_API_KEY })
app.post('/api/pyas-session', pyas.connect.sessionHandler())
Optional: Save your own user id on the account. If your app has its own id for each user, pass a function that returns the id of the user who is connecting. Pyas saves the id on the account as subjectId, so you can find that user's accounts later.
app.post('/api/pyas-session', pyas.connect.sessionHandler((req) => req.user.id))
Replace req.user.id with wherever your app stores the id of the signed-in user.
2. Add the component to your page
Give the component a function that calls your endpoint and returns the session token. When an account is connected, save its accountId:
import { PyasConnect } from '@pyas/connect-react'
const getSessionToken = () =>
fetch('/api/pyas-session', { method: 'POST' }).then((r) => r.json()).then((s) => s.token)
<PyasConnect getSessionToken={getSessionToken} onConnected={(account) => save(account.accountId)} />
PyasConnect shows a "Connect an account" button, with your app's name and icon from the dashboard. When a user connects an account, your handler receives the account:
{
"accountId": "6b0e3f1a-8c2d-4e5f-9a7b-1c3d5e7f9a2b",
"provider": "google",
"email": "avery@yourcustomer.com",
"name": "Avery Brooks",
"subjectId": "user_42"
}
With Native Auth, the provider redirects people to your callback URL instead of to Pyas. Add this code to your callback page so that it passes the result to the dialog:
import { postCallbackResult } from '@pyas/connect'
postCallbackResult()
The dialog is available in English, Norwegian, Swedish, Danish, German, French, Spanish, Dutch and Portuguese (Brazil). It uses your page's language (<html lang>), then the browser's. To choose the language yourself, set locale, for example locale="de". The pages Pyas shows in redirect mode work the same way: they use the browser's language, or add &locale=de to the session's url.
For themes, custom text, a custom button and every other option, see the package documentation on npm: @pyas/connect-react (opens in a new tab), @pyas/connect-vue (opens in a new tab), @pyas/connect-angular (opens in a new tab), and @pyas/connect (opens in a new tab) for any other page.
Option 2: Your own screens
With this option, you show the provider buttons yourself. When a user selects one, your server requests a sign-in URL from the API and redirects the user to it. After the user signs in, they return to your app with a code, and your server sends that code to the API to save the account.
First, set the page that users return to after they sign in. You do this under Provider setup in the dashboard:
- Pyas Auth: Set the provider's Redirect URL to the page in your app that receives the code. Pyas redirects people to that page after they sign in.
- Native Auth: Your callback URL is already set in Provider setup, and the provider redirects people to it.
1. Get a sign-in URL
When the user selects a provider button, request a sign-in URL from your server, then redirect the user to the url in the response:
import { Pyas } from '@pyas/sdk'
const pyas = new Pyas({ apiKey: process.env.PYAS_API_KEY })
const { url } = await pyas.connect.oauth({ provider: 'google', state: 'user_42' })
res.redirect(url)
{
"success": true,
"data": {
"provider": "google",
"url": "https://accounts.google.com/o/oauth2/v2/auth?client_id=...&redirect_uri=...&response_type=code&scope=...&state=..."
},
"provider": null,
"traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",
"meta": {}
}
| Parameter | Description |
|---|---|
provider | google, microsoft or zoom. Required. |
state | Any value you choose, such as your id for the user. It's returned to you with the code. |
loginHint | An email address to prefill on the provider's sign-in page. |
features | Additional access to request, as a comma-separated list of manageCalendars, sharing, meetingSettings, attachmentUploads and rooms. See Additional scopes. |
2. The user returns with a code
After signing in, the user is redirected to your Redirect URL (Pyas Auth) or your callback URL (Native Auth). The URL includes code and your state as query parameters:
https://yourapp.com/connected/google?code=4/0AeanS0Z...&state=user_42
If the user cancels or the provider denies access, there's no valid code, and the request in step 3 returns an error. Show your own message and let the user try again.
3. Exchange the code for an account
From your server, send the code to the API. The API saves the account and returns it:
const account = await pyas.connect.accounts.create({
provider: 'google',
code: req.query.code,
subjectId: req.query.state,
})
await saveAccount(user, account.accountId)
{
"success": true,
"data": {
"accountId": "6b0e3f1a-8c2d-4e5f-9a7b-1c3d5e7f9a2b",
"provider": "google",
"email": "avery@yourcustomer.com",
"name": "Avery Brooks",
"subjectId": "user_42",
"existing": false,
"grantedScopes": ["https://www.googleapis.com/auth/calendar"]
},
"provider": null,
"traceId": "trc_5f2a9c1e7b3d4a6f8e0c2b1d",
"meta": {}
}
subjectId is optional. It's your own id for the user, and the API saves it on the account. Each code can be used only once. If the request fails, the error explains why, for example with CONNECTION_CONSENT_FAILED or BILLING_CONNECTION_LIMIT. See Errors.
If you're coming from v1, steps 1 and 3 replace these two v1 endpoints:
GET /{provider}/oauth
POST /{provider}/connect
In v2, the provider is a parameter instead of part of the path. The v1 endpoints still work.
Save the account id
With either option, store the accountId with your user. Every request that reads or writes their calendar must include it. To find a user's accounts later, list them by the id you saved as subjectId:
for await (const account of pyas.accounts.list({ subjectId: user.id })) { /* ... */ }
Pyas also sends a connection.created webhook event for every new account.
When a connection breaks
A connection can stop working when a user revokes access, changes their password or leaves their company. To find the accounts that need attention, call:
GET /v2/accounts/status?overall=attention,broken
Each account in the response has an overall status:
overall.status | Meaning |
|---|---|
healthy | The connection works. |
attention | The connection works, but some features are limited, for example because of a missing permission. reason explains the limitation. |
broken | The account no longer syncs. |
The response is paginated, so keep requesting pages until meta.nextCursor is null. To check a single account, call:
GET /v2/accounts/{accountId}/status
When overall.fix is reconnect, the user needs to sign in again.
With PyasConnect, send the account's accountId from your page to your endpoint, and return it from the function you pass to sessionHandler. The dialog then opens directly to that account:
app.use(express.json())
app.post('/api/pyas-reconnect', pyas.connect.sessionHandler((req) => ({ accountId: req.body.accountId })))
With your own screens, send the same two requests again, first to get a sign-in URL and then to send the code. When the same person signs in, the API reconnects the account. The account keeps its accountId, and existing is true in the response.
Each account's status also includes userMessage, a sentence you can show the user. The connection.broken and connection.reauthorized webhook events notify you when an account stops syncing and when it's reconnected.