Making Requests
Every Driftwood API call follows the same pattern: send a POST request with a JSON body, get back a JSON response.
Request Format
Section titled “Request Format”All API calls must:
- Use the POST method
- Send a JSON body (even if empty — send
{}) - Include
Content-Type: application/json - Include
Authorization: Bearer TOKEN(for authenticated endpoints)
curl -X POST https://api.driftwoodapp.com/api/{operation-name} \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ ... }'Operation Names
Section titled “Operation Names”Each API operation has a unique name used as the URL path:
| Pattern | Example | Description |
|---|---|---|
{resource}-list | contacts-list | List/search records |
{resource}-get | contacts-get | Get a single record |
{resource}-create | contacts-create | Create a record |
{resource}-update | contacts-update | Update a record |
{resource}-delete | contacts-delete | Delete a record |
Response Format
Section titled “Response Format”Success
Section titled “Success”{ "ok": true, "result": { "id": "550e8400-e29b-41d4-a716-446655440000", "first_name": "Jane", "last_name": "Smith" }}{ "ok": false, "error": { "code": "contacts.not_found", "message": "Contact not found" }}Always check the ok field first. The HTTP status code will also reflect the error type (400, 401, 403, 404, 429, 500).
Data Types
Section titled “Data Types”| Type | Format | Example |
|---|---|---|
| UUID | RFC 4122 string | "550e8400-e29b-41d4-a716-446655440000" |
| Datetime | ISO 8601 / RFC 3339 | "2025-06-01T12:00:00Z" |
| Date | YYYY-MM-DD string | "2025-06-01" |
| Tags | Array of strings | ["vip", "enterprise"] |
| Custom fields | JSON object | {"field_uuid": "value"} |
| Currency/money | Integer (cents) | 150000 = $1,500.00 |
Partial Updates
Section titled “Partial Updates”Update operations accept partial payloads. Only include the fields you want to change:
# Only update the email — all other fields remain unchangedcurl -X POST https://api.driftwoodapp.com/api/contacts-update \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "id": "550e8400-e29b-41d4-a716-446655440000", "email": "new-email@example.com" }'Empty Requests
Section titled “Empty Requests”Some endpoints don’t require parameters. Always send an empty JSON object:
curl -X POST https://api.driftwoodapp.com/api/sources-list \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{}'Examples in Different Languages
Section titled “Examples in Different Languages”Python
Section titled “Python”import requests
def driftwood_api(operation, token, body=None): response = requests.post( f"https://api.driftwoodapp.com/api/{operation}", headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", }, json=body or {}, ) data = response.json() if not data["ok"]: raise Exception(f"{data['error']['code']}: {data['error']['message']}") return data["result"]
# Usagecontacts = driftwood_api("contacts-list", token, {"limit": 10})JavaScript / TypeScript
Section titled “JavaScript / TypeScript”async function driftwoodApi(operation: string, token: string, body?: object) { const response = await fetch( `https://api.driftwoodapp.com/api/${operation}`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify(body ?? {}), } ); const data = await response.json(); if (!data.ok) { throw new Error(`${data.error.code}: ${data.error.message}`); } return data.result;}
// Usageconst contacts = await driftwoodApi("contacts-list", token, { limit: 10 });func driftwoodAPI(operation, token string, body any) (json.RawMessage, error) { payload, _ := json.Marshal(body) req, _ := http.NewRequest("POST", "https://api.driftwoodapp.com/api/"+operation, bytes.NewReader(payload)) req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close()
var result struct { OK bool `json:"ok"` Result json.RawMessage `json:"result"` Error *struct { Code string `json:"code"` Message string `json:"message"` } `json:"error"` } json.NewDecoder(resp.Body).Decode(&result) if !result.OK { return nil, fmt.Errorf("%s: %s", result.Error.Code, result.Error.Message) } return result.Result, nil}