Use OAuth 2.0 when you are building a third-party integration that acts on
behalf of one or more Apptoto users. If you are writing a script that calls
the API as yourself, HTTP Basic Authentication
is simpler.
Register an OAuth Application
In the Apptoto portal, go to Settings > Integrations > OAuth Applications
and click Register OAuth Application. Fill in:
- Name: a friendly name shown to users on the consent screen.
- Redirect URI: the URL Apptoto sends the user back to after they approve
the consent screen. Must match exactly when you start the flow. - Server-Side Application: leave on for apps that run on a server you
control (Rails, Node, Python, etc.) and can keep a secret. Turn off for
mobile, desktop, or single-page apps - they should use PKCE instead.
When you create the app, the Client ID and Client Secret are shown
once. Copy the secret immediately - if you lose it, delete the application
and register a new one.
Single-Tenant by Default
Newly registered OAuth applications are private to the user that registered
them. Only that user can complete the /oauth/authorize flow against the
app. This lets you build and test integrations privately without exposing
them to other Apptoto accounts.
When your integration is ready for general use, email
[email protected] to request publication.
Once published, any logged-in Apptoto user can authorize the app.
Authorization Code Flow
OAuth on Apptoto uses the standard authorization_code flow.
1. Send the user to the consent screen
Direct the user's browser to:
https://www.apptoto.com/oauth/authorize?
client_id=YOUR_CLIENT_ID&
redirect_uri=YOUR_REDIRECT_URI&
response_type=code&
scope=api
After the user clicks Authorize, Apptoto redirects to your
redirect_uri with a one-time code parameter:
https://yourapp.example.com/oauth/callback?code=ABC123
2. Exchange the code for an access token
curl -X POST https://api.apptoto.com/oauth/token \
-d grant_type=authorization_code \
-d code=ABC123 \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET \
-d redirect_uri=YOUR_REDIRECT_URI
Response:
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 2592000,
"refresh_token": "...",
"scope": "api",
"created_at": 1747507200
}
3. Call the API with the access token
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
https://api.apptoto.com/v1/userinfo
Every /v1/* endpoint accepts OAuth Bearer tokens, including the ones
documented elsewhere in this reference.
4. Refresh an expired access token
Access tokens are valid for 30 days. When they expire, use the
refresh_token to mint a new one:
curl -X POST https://api.apptoto.com/oauth/token \
-d grant_type=refresh_token \
-d refresh_token=YOUR_REFRESH_TOKEN \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET
Refresh tokens rotate - each refresh response contains a new
refresh_token that replaces the previous one.
Scopes
Apptoto uses granular per-domain scopes. Each scope grants access to
the endpoints listed below. Your application must declare every scope it
needs when you register it (Settings > Integrations > OAuth Applications),
and request the same scopes (or a subset) in the authorize URL.
Read scopes
| Scope | Endpoints |
|---|---|
calendars:read | GET /v1/calendars |
address_books:read | GET /v1/address_books |
contacts:read | GET /v1/contact, GET /v1/contacts |
events:read | GET /v1/event, GET /v1/events |
bookings:read | GET /v1/availability, GET /v1/bookings, GET /v1/booking_pages |
imports:read | GET /v1/import |
lists:read | GET /v1/lists |
messaging:read | GET /v1/conversations, GET /v1/conversation_messages |
Write scopes
| Scope | Endpoints |
|---|---|
contacts:write | POST / PUT / DELETE /v1/contacts |
events:write | POST / PUT / DELETE /v1/events, POST /v1/event/mark |
bookings:write | POST /v1/book_now |
imports:write | POST /v1/import -- bulk-creates contacts/events; high impact. |
lists:write | POST / PUT / DELETE /v1/lists |
messaging:send | POST /v1/start_conversation, POST /v1/conversation_reply -- sends SMS / email; billed and reaches end users. |
Always available
GET /v1/userinfo requires only a valid token; it is not scope-gated.
Requesting multiple scopes
OAuth 2.0 scopes are space-delimited in the scope query parameter; URL-
encode the space as + or %20:
https://www.apptoto.com/oauth/authorize?
client_id=YOUR_CLIENT_ID&
redirect_uri=YOUR_REDIRECT_URI&
response_type=code&
scope=contacts:read+events:read+events:write
Calling an endpoint with a token that lacks the required scope returns:
HTTP/1.1 403 Forbidden
{"errors":"insufficient_scope","required_scope":"events:write"}
Revoking an Access Token
Programmatic revoke:
curl -X POST https://api.apptoto.com/oauth/revoke \
-d token=YOUR_ACCESS_TOKEN \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET
Users can also revoke an application's access from the Apptoto portal
under Settings > Integrations > OAuth Applications > Connected Applications.
Linked Users
If the authorizing user has another user account set as their "API target"
in Apptoto, OAuth requests are forwarded to that target user automatically -
the same way HTTP Basic Authentication works. This means an integration
authorized by an agent account will act on the parent account's data when
the agent is configured that way.
