Using API v1? See what changes in v2, or the v1 docs
Authentication
Every request must include one of your app's API keys in the x-api-key header. You create keys in the dashboard, under your app's API keys.
curl https://api.pyas.io/v2/accounts \
-H "x-api-key: pyas_sk_..."
Send the key only in the x-api-key header. If you send it in an Authorization: Bearer header instead, the API rejects the request with AUTH_INVALID_KEY, and the error's hint explains which header to use.
Keep your API keys on your server. Code that runs in the browser never needs a key, even to connect accounts. See Connecting accounts.
The API refuses a live key sent from a web page with AUTH_KEY_IN_BROWSER, and emails your app's owner the first time it happens, because anyone who opens the page can read the key. A test key sent from a web page still works, but the response includes a Pyas-Warning header. Either way, the dashboard's API keys page shows which key was sent from a web page, and from which site. Requests from a server don't include an Origin header, so they are never affected.
Live and test keys
| Live key | Test key | |
|---|---|---|
| Starts with | pyas_sk_ | pyas_sk_test_ |
| Has access to | Your users' connected calendars | Your app's sandbox only |
| Counts toward | Your plan's monthly requests | A separate monthly allowance for testing |
| Can connect accounts | Yes | No. The sandbox has its own accounts. |
Live and test keys are kept separate. A test key can't read a real calendar, and a live key can't access the sandbox. Code that you write with a test key works without changes when you switch to a live key.
Scopes
A key has the read scope, the write scope, or both. The read scope allows GET requests, and the two POST requests that only read: free/busy and meeting times (see Availability). Every other request requires the write scope, and so does reading a webhook's signing secret (GET /v2/webhooks/{webhookId}/secret). If a key doesn't have the scope that a request requires, the API returns AUTH_INSUFFICIENT_SCOPE with a 403 Forbidden status.
For example, you can give a reporting job a read-only key and grant write only to the services that write to calendars.
Rotating a key
When you rotate a key, the API creates a new key and keeps the old one working for a grace period. This lets you deploy the new key without any failed requests.
The response includes the new key's value in key, the new key's details in newKey, and the old key's details in previousKey. The old key's value isn't returned: it's the key you sent the request with.
// Send this request with the key you want to replace.
const { key, newKey, previousKey } = await pyas.keys.rotate({ keyId: 'current', graceHours: 24 })
// key: the NEW key's value, such as 'pyas_sk_live_...'. Store it now, in place of the old
// key: it's returned only in this response.
// newKey: the new key's details (keyId, last4, scopes). Its value is key, above.
// previousKey: the key you just rotated. It keeps working until previousKey.graceEndsAt,
// 24 hours from now, so you have time to deploy the new key.
currentrefers to the key that you send the request with. A key can rotate only itself, so to rotate a different key, send the request with that key or use the dashboard.- The grace period is 24 hours by default, with a maximum of 168 hours. A value of
0makes the old key stop working immediately. - During the grace period, every response to a request made with the old key includes a
Pyas-Key-Expires-Atheader. You can search your logs for this header to find any service you forgot to update. - Pyas sends an
api_key.expiringwebhook event 1 hour before the old key stops working, and anapi_key.revokedevent when it stops working. - You can rotate a key only once. The next time you rotate, rotate the new key.
If a key has an expiration date and you don't rotate it, you receive an api_key.expiring event and an email 7 days before it expires.
Authentication errors
| Code | Status | Meaning |
|---|---|---|
AUTH_INVALID_KEY | 401 | The key is missing, unknown or revoked, was sent somewhere other than the x-api-key header, or was replaced by a rotation whose grace period has ended |
AUTH_EXPIRED_KEY | 401 | The key has passed the expiry date it was created with |
AUTH_INSUFFICIENT_SCOPE | 403 | The key doesn't have the scope that this request requires |
AUTH_KEY_IN_BROWSER | 403 | A live key was sent from a web page. Make the request from your server, and rotate the key |
AUTH_SANDBOX_MISMATCH | 403 | A test key was used for an operation that only a live key can perform, or the reverse |