Request & Response Format

Direct Answer

All [API_NAME] requests use JSON format with standard HTTP headers. Responses follow consistent structure with resource objects, timestamps in ISO 8601, and standardized error format.


Request Format

Content Type

All POST, PATCH, and PUT requests must use JSON:

Content-Type: application/json

Request Body

Valid JSON:

{
  "field1": "string value",
  "field2": 123,
  "field3": true,
  "nested": {
    "field4": "nested value"
  },
  "array": ["item1", "item2"]
}

Data Types:

TypeExampleNotes
String"value"UTF-8 encoded, max 10,000 chars
Number123 or 123.45Integer or float
Booleantrue or falseLowercase only
NullnullRepresents absence of value
Object{"key": "value"}Nested objects supported
Array["item1", "item2"]Arrays of any type

Special Fields

Timestamps:

Always use ISO 8601 format with timezone:

{
  "scheduled_at": "2024-01-15T10:30:00Z",
  "expires_at": "2024-12-31T23:59:59Z"
}

Dates:

{
  "birth_date": "1990-06-15",  // YYYY-MM-DD
  "start_date": "2024-01"       // YYYY-MM (month precision)
}

Amounts:

Use smallest currency unit (cents):

{
  "amount": 1999,      // $19.99
  "currency": "usd"    // ISO 4217 currency code
}

IDs:

Always strings with prefix:

{
  "user_id": "usr_abc123",
  "workspace_id": "ws_xyz789"
}

Metadata:

Custom key-value data:

{
  "metadata": {
    "customer_id": "12345",
    "order_ref": "ORD-2024-001",
    "notes": "VIP customer"
  }
}

Limits:

  • Max 50 keys
  • Key names: 40 characters max
  • Values: 500 characters max


Response Format

Success Response

Single Resource:

{
  "id": "usr_abc123",
  "object": "user",
  "email": "user@example.com",
  "name": "John Doe",
  "role": "member",
  "status": "active",
  "metadata": {
    "department": "engineering"
  },
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:30:00Z"
}

Standard Fields:

FieldTypeDescription
idstringUnique identifier
objectstringResource type ("user", "document", etc.)
created_atstringISO 8601 creation timestamp
updated_atstringISO 8601 last update timestamp
Collection (List):
{
  "object": "list",
  "data": [
    {
      "id": "usr_abc123",
      "object": "user",
      ...
    },
    {
      "id": "usr_def456",
      "object": "user",
      ...
    }
  ],
  "has_more": true,
  "url": "/v1/users"
}

Learn about pagination →

Error Response

{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Required parameter 'email' is missing",
    "details": {
      "parameter": "email",
      "expected_type": "string"
    },
    "request_id": "req_abc123"
  }
}

Full error reference →


HTTP Status Codes

2xx Success

200 OK

Standard successful response:

HTTP/1.1 200 OK
Content-Type: application/json

{ "id": "usr_123", "email": "user@example.com", ... }

201 Created

Resource successfully created:

HTTP/1.1 201 Created
Location: https://api.example.com/v1/users/usr_new123
Content-Type: application/json

{ "id": "usr_new123", "email": "newuser@example.com", ... }

204 No Content

Successful request with no response body:

HTTP/1.1 204 No Content

Used for DELETE operations that don't return content.

4xx Client Errors

400 Bad Request

Invalid request format:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{ "error": { "code": "INVALID_JSON", "message": "Request body is not valid JSON" } }

401 Unauthorized

Authentication required or failed:

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{ "error": { "code": "INVALID_API_KEY", "message": "The provided API key is invalid" } }

404 Not Found

Resource doesn't exist:

HTTP/1.1 404 Not Found
Content-Type: application/json

{ "error": { "code": "RESOURCE_NOT_FOUND", "message": "No user found with ID 'usr_invalid'" } }

5xx Server Errors

500 Internal Server Error

Unexpected server error:

HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{ "error": { "code": "INTERNAL_ERROR", "message": "An unexpected error occurred", "request_id": "req_abc123" } }


Response Headers

Standard Headers

Content-Type: application/json; charset=utf-8
X-Request-ID: req_abc123xyz456
Date: Mon, 15 Jan 2024 10:30:00 GMT

Rate Limit Headers

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1704445200

Pagination Headers

Link: <https://api.example.com/v1/users?starting_after=usr_xyz>; rel="next"

Cache Headers

Cache-Control: max-age=300, private
ETag: "686897696a7c876b7e"
Last-Modified: Mon, 15 Jan 2024 10:00:00 GMT

Field Formatting

Naming Conventions

Use snake_case for all field names:

Good:

{
  "user_id": "usr_123",
  "created_at": "2024-01-15T10:30:00Z",
  "is_active": true
}

Bad:

{
  "userId": "usr_123",
  "CreatedAt": "2024-01-15T10:30:00Z",
  "IsActive": true
}

Date & Time

Always use ISO 8601 with UTC timezone:

{
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T11:45:30.123Z",  // With milliseconds
  "scheduled_for": "2024-02-01T09:00:00Z"
}

Parsing in JavaScript:

const date = new Date(response.created_at);
console.log(date.toLocaleString());

Parsing in Python:

from datetime import datetime

date = datetime.fromisoformat(response['created_at'].replace('Z', '+00:00')) print(date)

Money & Currency

Use smallest unit (cents, pence, etc.):

{
  "amount": 1999,        // $19.99
  "currency": "usd",     // ISO 4217 code
  "formatted": "$19.99"  // Optional human-readable
}

Supported Currencies:

  • usd - US Dollar
  • eur - Euro
  • gbp - British Pound
  • jpy - Japanese Yen
  • [See full list →]

Enums

Use lowercase strings:

{
  "status": "active",      // not "ACTIVE"
  "role": "admin",         // not "Admin"
  "priority": "high"       // not "HIGH"
}

Common Enums:

{
  "status": "active" | "inactive" | "pending" | "deleted",
  "role": "owner" | "admin" | "member" | "viewer",
  "visibility": "public" | "private" | "shared"
}

Booleans

Always true or false (lowercase):

{
  "is_active": true,
  "email_verified": false,
  "deleted": false
}

Null Values

Use null for absent optional values:

{
  "description": null,      // No description set
  "deleted_at": null,       // Not deleted
  "avatar_url": null        // No avatar
}

Omitting vs Null:

In responses, we include fields with null values for clarity:

{
  "id": "usr_123",
  "name": "John Doe",
  "bio": null,           // Explicit: no bio
  "avatar_url": null     // Explicit: no avatar
}

In requests, you can omit optional fields:

{
  "name": "John Doe"
  // bio and avatar_url omitted
}

Nested Objects

Embedded Objects

Small related objects can be embedded:

{
  "id": "doc_123",
  "title": "Document Title",
  "author": {
    "id": "usr_456",
    "name": "John Doe",
    "email": "john@example.com"
  },
  "workspace": {
    "id": "ws_789",
    "name": "Engineering"
  }
}

ID References

Large or frequently changing objects use ID references:

{
  "id": "doc_123",
  "title": "Document Title",
  "author_id": "usr_456",      // Reference by ID
  "workspace_id": "ws_789"     // Reference by ID
}

Expanding References:

Use expand parameter to get full objects:

GET /v1/documents/doc_123?expand=author,workspace

Returns:

{
  "id": "doc_123",
  "title": "Document Title",
  "author_id": "usr_456",
  "author": {                  // Expanded
    "id": "usr_456",
    "name": "John Doe",
    ...
  },
  "workspace_id": "ws_789",
  "workspace": {               // Expanded
    "id": "ws_789",
    "name": "Engineering",
    ...
  }
}

Arrays

Simple Arrays

{
  "tags": ["important", "urgent", "customer"],
  "permissions": ["read", "write", "delete"],
  "numbers": [1, 2, 3, 4, 5]
}

Object Arrays

{
  "members": [
    {
      "user_id": "usr_123",
      "role": "admin",
      "joined_at": "2024-01-01T00:00:00Z"
    },
    {
      "user_id": "usr_456",
      "role": "member",
      "joined_at": "2024-01-05T00:00:00Z"
    }
  ]
}

Empty Arrays

Return empty array [], not null:

Good:

{
  "tags": [],
  "members": []
}

Bad:

{
  "tags": null,
  "members": null
}


Partial Responses

Field Selection

Request only needed fields:

GET /v1/users/usr_123?fields=id,email,name

Response:

{
  "id": "usr_123",
  "email": "user@example.com",
  "name": "John Doe"
  // Other fields omitted
}

Benefits:

  • Reduced payload size
  • Faster responses
  • Lower bandwidth usage

Limitations:

  • id and object always included
  • Invalid field names ignored


Pagination Response

{
  "object": "list",
  "data": [
    {...},
    {...}
  ],
  "has_more": true,
  "url": "/v1/resources",
  "next_cursor": "usr_xyz789"
}

Full pagination guide →


Versioning in Responses

Resource version tracking:

{
  "id": "usr_123",
  "version": 5,              // Increments on each update
  "updated_at": "2024-01-15T11:00:00Z"
}

Optimistic Locking:

PATCH /v1/users/usr_123
If-Match: 5  # Only update if current version is 5

{ "name": "New Name" }

Success:

{
  "id": "usr_123",
  "version": 6,  // Incremented
  "name": "New Name"
}

Conflict:

HTTP/1.1 409 Conflict

{ "error": { "code": "VERSION_CONFLICT", "message": "Resource was modified by another request", "current_version": 7 } }


Webhooks Response

Webhook payloads follow same format:

{
  "id": "evt_abc123",
  "object": "event",
  "type": "user.created",
  "created": 1704441600,
  "data": {
    "object": {
      "id": "usr_new123",
      "object": "user",
      ...
    }
  }
}

Webhooks documentation →


Best Practices

1. Consistent Structure

All resources follow same pattern:

{
  "id": "...",
  "object": "...",
  ...resource fields...,
  "metadata": {...},
  "created_at": "...",
  "updated_at": "..."
}

2. Meaningful Field Names

Good:

{
  "email_verified": true,
  "last_login_at": "2024-01-15T10:00:00Z",
  "profile_image_url": "https://..."
}

Bad:

{
  "ev": true,
  "ll": "2024-01-15T10:00:00Z",
  "img": "https://..."
}

3. Don't Abbreviate

Good:

{
  "description": "...",
  "maximum_allowed": 100,
  "is_administrator": true
}

Bad:

{
  "desc": "...",
  "max_allow": 100,
  "is_admin": true
}

4. Return Created Resource

On POST, return the created resource:

POST /v1/users
{
  "email": "user@example.com"
}

Response: 201 Created { "id": "usr_new123", "email": "user@example.com", "created_at": "2024-01-15T10:30:00Z", ... }

5. Validate Input

Return detailed validation errors:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": {
      "email": ["Email is required", "Email format is invalid"],
      "age": ["Age must be at least 18"],
      "password": ["Password must be at least 8 characters"]
    }
  }
}

Character Encoding

All requests and responses use UTF-8:

Content-Type: application/json; charset=utf-8

Supported Characters:

  • ✅ ASCII
  • ✅ Unicode (UTF-8)
  • ✅ Emoji: 🎉 😊 ✅
  • ✅ International: 中文, العربية, עברית

Example:

{
  "name": "François Müller",
  "bio": "Software engineer 👨‍💻",
  "location": "東京"
}

Content Negotiation

Request Format

POST /v1/users
Content-Type: application/json

{"email": "user@example.com"}

Response Format

Default is JSON. Specify alternative via Accept header:

GET /v1/users/usr_123
Accept: application/json  # Default

Currently Supported:

  • application/json (default)

Future Support:

  • application/xml
  • text/csv (for exports)


Compression

Responses support gzip compression:

Request:

GET /v1/users
Accept-Encoding: gzip

Response:

HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Type: application/json

[Compressed data]

Benefits:

  • 70-90% size reduction
  • Faster transfers
  • Lower bandwidth costs

Recommendation: Always send Accept-Encoding: gzip header.


Related Documentation


Questions? Contact Support

Last Updated: [DATE]