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 keyTest key
Starts withpyas_sk_pyas_sk_test_
Has access toYour users' connected calendarsYour app's sandbox only
Counts towardYour plan's monthly requestsA separate monthly allowance for testing
Can connect accountsYesNo. 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.
  • current refers 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 0 makes 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-At header. You can search your logs for this header to find any service you forgot to update.
  • Pyas sends an api_key.expiring webhook event 1 hour before the old key stops working, and an api_key.revoked event 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

CodeStatusMeaning
AUTH_INVALID_KEY401The 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_KEY401The key has passed the expiry date it was created with
AUTH_INSUFFICIENT_SCOPE403The key doesn't have the scope that this request requires
AUTH_KEY_IN_BROWSER403A live key was sent from a web page. Make the request from your server, and rotate the key
AUTH_SANDBOX_MISMATCH403A test key was used for an operation that only a live key can perform, or the reverse