OAuth Apps
OAuth apps let you build server-to-server integrations with Driftwood. They use the client credentials grant type — your app exchanges a client ID and secret for an access token, then uses that token to call the API.
How It Works
Section titled “How It Works”┌─────────────┐ ┌──────────────────┐│ Your App │ │ Driftwood API ││ │ 1. POST oauth-token │ ││ │──────────────────────→ │ ││ │ client_id + secret │ ││ │ │ ││ │ ←──────────────────── │ ││ │ 2. access_token │ ││ │ │ ││ │ 3. API calls with │ ││ │──────────────────────→ │ ││ │ Bearer token │ │└─────────────┘ └──────────────────┘Creating an OAuth App
Section titled “Creating an OAuth App”You can create OAuth apps through the Driftwood UI (Settings → OAuth Apps) or via the API.
Via API
Section titled “Via API”curl -X POST https://api.driftwoodapp.com/api/oauth-apps-create \ -H "Authorization: Bearer YOUR_USER_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "My CRM Sync"}'Response:
{ "ok": true, "result": { "app": { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "account_id": "a1b2c3d4-...", "created_by": "u1v2w3x4-...", "name": "My CRM Sync", "client_id": "dw_ci_k8j2m4n6p8q0", "client_secret_prefix": "dw_cs_", "created_at": "2025-06-01T12:00:00Z", "updated_at": "2025-06-01T12:00:00Z" }, "client_secret": "dw_cs_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" }}Getting an Access Token
Section titled “Getting an Access Token”Exchange your credentials for a token using the oauth-token endpoint:
curl -X POST https://api.driftwoodapp.com/api/oauth-token \ -H "Content-Type: application/json" \ -d '{ "grant_type": "client_credentials", "client_id": "dw_ci_k8j2m4n6p8q0", "client_secret": "dw_cs_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" }'Response:
{ "ok": true, "result": { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "bearer", "expires_in": 3600 }}Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
grant_type | string | Yes | Must be "client_credentials" |
client_id | string | Yes | Your app’s client ID |
client_secret | string | Yes | Your app’s client secret |
Using the Token
Section titled “Using the Token”Include the token in the Authorization header of all API requests:
curl -X POST https://api.driftwoodapp.com/api/contacts-list \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \ -H "Content-Type: application/json" \ -d '{"limit": 25}'Token Refresh Strategy
Section titled “Token Refresh Strategy”OAuth access tokens expire after 1 hour. Your integration should:
- Cache the token and its expiration time
- Before each API call, check if the token is near expiry (e.g., within 5 minutes)
- If expired or near-expiry, request a new token
import timeimport requests
class DriftwoodClient: def __init__(self, client_id, client_secret): self.client_id = client_id self.client_secret = client_secret self.token = None self.token_expires_at = 0
def get_token(self): if self.token and time.time() < self.token_expires_at - 300: return self.token
resp = requests.post( "https://api.driftwoodapp.com/api/oauth-token", json={ "grant_type": "client_credentials", "client_id": self.client_id, "client_secret": self.client_secret, }, ) data = resp.json()["result"] self.token = data["access_token"] self.token_expires_at = time.time() + data["expires_in"] return self.token
def call(self, operation, body=None): resp = requests.post( f"https://api.driftwoodapp.com/api/{operation}", headers={"Authorization": f"Bearer {self.get_token()}"}, json=body or {}, ) return resp.json()Permissions and Scope
Section titled “Permissions and Scope”OAuth app tokens inherit the permissions of the user who created the app:
- They can access all data within the user’s account
- They are subject to the same subscription and account-lock checks
- They cannot perform admin operations
Rate Limits
Section titled “Rate Limits”OAuth apps have higher rate limits than user tokens:
| Auth Type | Rate | Burst |
|---|---|---|
| User token | 10 req/s | 20 |
| OAuth token | 50 req/s | 100 |
See Rate Limits for details.
Managing Apps
Section titled “Managing Apps”List OAuth Apps
Section titled “List OAuth Apps”curl -X POST https://api.driftwoodapp.com/api/oauth-apps-list \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"include_revoked": false}'Revoke an OAuth App
Section titled “Revoke an OAuth App”Revoking an app immediately invalidates all its tokens. This cannot be undone.
curl -X POST https://api.driftwoodapp.com/api/oauth-apps-revoke \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"}'OAuth App Object
Section titled “OAuth App Object”| Field | Type | Description |
|---|---|---|
id | uuid | Unique identifier |
account_id | uuid | Account the app belongs to |
created_by | uuid | User who created the app |
name | string | Display name |
client_id | string | Public client identifier (prefix: dw_ci_) |
client_secret_prefix | string | First characters of the secret for identification |
revoked_at | datetime | null | When the app was revoked, if applicable |
last_used_at | datetime | null | Last time the app authenticated |
created_at | datetime | Creation timestamp |
updated_at | datetime | Last update timestamp |
Error Codes
Section titled “Error Codes”| Code | Description |
|---|---|
oauth.invalid_request | Malformed request body |
oauth.unsupported_grant_type | Only client_credentials is supported |
oauth.missing_credentials | Missing client_id or client_secret |
oauth.invalid_client | Invalid credentials or revoked app |
oauth.internal | Server error |