StateSet API Design Guidelines

This document outlines the design principles, patterns, and best practices for the StateSet API. Following these guidelines ensures consistency, usability, and maintainability across all API endpoints.

Core Principles

1. RESTful Design

  • Use standard HTTP methods appropriately:
    • GET for retrieving resources
    • POST for creating resources
    • PUT/PATCH for updating resources
    • DELETE for removing resources

2. Resource-Oriented URLs

Good:
GET    /v1/orders
GET    /v1/orders/{id}
POST   /v1/orders
PUT    /v1/orders/{id}
DELETE /v1/orders/{id}

Avoid:
GET    /v1/getOrders
POST   /v1/createOrder
POST   /v1/orders/update

3. Consistent Naming Conventions

  • Use lowercase with hyphens for URLs: /v1/work-orders
  • Use camelCase for JSON properties: firstName, createdAt
  • Use snake_case for query parameters: created_after, sort_by
  • Pluralize collection endpoints: /orders not /order

Request Standards

Headers

Required headers for all requests:
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Accept: application/json
Optional headers:
X-API-Version: 2024-01-01
X-Idempotency-Key: unique-request-id
X-Request-ID: client-generated-id
Accept-Language: en-US

Query Parameters

Pagination

All list endpoints must support:
GET /v1/orders?limit=20&offset=0
GET /v1/orders?limit=20&cursor=eyJpZCI6MTAwfQ==
Default pagination:
  • limit: 20 (max: 100)
  • offset: 0

Filtering

Use consistent filter patterns:
# Exact match
GET /v1/orders?status=shipped

# Multiple values
GET /v1/orders?status_in=shipped,delivered

# Range queries
GET /v1/orders?created_after=2024-01-01T00:00:00Z
GET /v1/orders?created_before=2024-12-31T23:59:59Z
GET /v1/orders?amount_gte=10000
GET /v1/orders?amount_lte=50000

# Search
GET /v1/orders?search=john+doe
GET /v1/orders?customer_email=john@example.com

Sorting

GET /v1/orders?sort=created_at&order=desc
GET /v1/orders?sort=-created_at  # Alternative: prefix with - for desc

Request Body

Required Fields

Clearly mark required fields in documentation:
{
  "customer": {       // required
    "email": "...",   // required
    "name": "..."     // optional
  }
}

Nested Objects

Use nested objects for logical grouping:
{
  "customer": {
    "email": "john@example.com",
    "first_name": "John",
    "last_name": "Doe"
  },
  "shipping_address": {
    "line1": "123 Main St",
    "city": "San Francisco",
    "state": "CA",
    "postal_code": "94105",
    "country": "US"
  }
}

Response Standards

Success Responses

Single Resource

{
  "id": "ord_1a2b3c4d",
  "object": "order",
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:30:00Z",
  // ... resource fields
}

Collection

{
  "object": "list",
  "data": [
    {
      "id": "ord_1a2b3c4d",
      "object": "order",
      // ... resource fields
    }
  ],
  "has_more": true,
  "total_count": 150,
  "url": "/v1/orders"
}

With Metadata

{
  "data": {
    "id": "ord_1a2b3c4d",
    "object": "order",
    // ... resource fields
  },
  "meta": {
    "request_id": "req_xyz789",
    "version": "v1",
    "timestamp": "2024-01-15T10:30:00Z"
  }
}

Error Responses

Standard Error Format

{
  "error": {
    "type": "validation_error",
    "code": "VALIDATION_ERROR",
    "message": "Invalid request parameters",
    "details": {
      "field_errors": {
        "email": "Invalid email format",
        "quantity": "Must be a positive integer"
      }
    },
    "documentation_url": "https://docs.stateset.com/errors/VALIDATION_ERROR",
    "request_id": "req_xyz789"
  }
}

HTTP Status Codes

StatusUsage
200 OKSuccessful GET, PUT, PATCH
201 CreatedSuccessful POST creating resource
202 AcceptedRequest accepted for async processing
204 No ContentSuccessful DELETE
400 Bad RequestInvalid request parameters
401 UnauthorizedMissing or invalid authentication
403 ForbiddenValid auth but insufficient permissions
404 Not FoundResource doesn’t exist
409 ConflictResource conflict (e.g., duplicate)
422 Unprocessable EntityValidation errors
429 Too Many RequestsRate limit exceeded
500 Internal Server ErrorServer error
503 Service UnavailableTemporary unavailability

Data Types and Formats

Timestamps

Always use ISO 8601 format with timezone:
{
  "created_at": "2024-01-15T10:30:00Z",
  "scheduled_for": "2024-01-20T14:00:00-08:00"
}

Money/Currency

Store monetary values in smallest currency unit (cents):
{
  "amount": 1999,  // $19.99
  "currency": "USD"
}

Phone Numbers

Use E.164 format:
{
  "phone": "+14155551234"
}

Countries and States

Use ISO standards:
  • Countries: ISO 3166-1 alpha-2 (US, CA, GB)
  • States/Provinces: ISO 3166-2 (CA-ON, US-NY)
{
  "country": "US",
  "state": "CA"
}

Versioning

URL Versioning

Primary versioning method:
https://api.stateset.com/v1/orders
https://api.stateset.com/v2/orders

Header Versioning

For minor versions:
X-API-Version: 2024-01-01

Deprecation Process

  1. Announce deprecation with 90-day notice
  2. Add deprecation headers:
    Sunset: Sat, 31 Dec 2024 23:59:59 GMT
    Deprecation: true
    Link: <https://api.stateset.com/v2/orders>; rel="successor-version"
    
  3. Maintain deprecated version for minimum 12 months

Idempotency

Implementation

Support idempotency for all POST, PUT, PATCH requests:
POST /v1/orders
X-Idempotency-Key: unique-request-id-123
Response includes:
X-Idempotency-Key: unique-request-id-123
X-Idempotent-Replayed: true

Webhooks

Event Naming

Use dot notation for event types:
order.created
order.updated
order.shipped
order.delivered
return.requested
return.approved
payment.succeeded
payment.failed

Webhook Payload

{
  "id": "evt_1a2b3c4d",
  "type": "order.created",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "object": {
      "id": "ord_xyz789",
      "object": "order",
      // ... full object
    },
    "previous_attributes": {
      // ... for update events
    }
  },
  "request": {
    "id": "req_abc123",
    "idempotency_key": "unique-key-123"
  }
}

Security

Always include signature header:
X-StateSet-Signature: sha256=3f3b3c4d...

Performance Guidelines

Response Times

Target response times:
  • Simple reads: < 200ms
  • Complex queries: < 500ms
  • Writes: < 1000ms

Payload Size

  • Limit response size to 1MB
  • Use pagination for large collections
  • Support field filtering:
    GET /v1/orders?fields=id,status,customer
    

Caching

Include cache headers:
Cache-Control: private, max-age=300
ETag: "33a64df551"
Last-Modified: Wed, 15 Jan 2024 10:30:00 GMT

GraphQL Guidelines

Query Naming

# Good
query GetOrder($id: ID!) {
  order(id: $id) {
    id
    status
  }
}

# Avoid
query fetchOrderData($id: ID!) {
  getOrderById(id: $id) {
    id
    status
  }
}

Mutations

mutation CreateOrder($input: OrderCreateInput!) {
  orderCreate(input: $input) {
    order {
      id
      status
    }
    userErrors {
      field
      message
    }
  }
}

Error Handling

Return errors in userErrors field:
{
  "data": {
    "orderCreate": {
      "order": null,
      "userErrors": [
        {
          "field": "customer.email",
          "message": "Email is required"
        }
      ]
    }
  }
}

Testing

Test Coverage

All endpoints must include:
  • Success path tests
  • Error handling tests
  • Edge case tests
  • Performance tests

Example Test Cases

describe('POST /v1/orders', () => {
  test('creates order successfully', async () => {
    const response = await api.post('/v1/orders', validOrderData);
    expect(response.status).toBe(201);
    expect(response.body.object).toBe('order');
  });

  test('returns 400 for invalid data', async () => {
    const response = await api.post('/v1/orders', invalidOrderData);
    expect(response.status).toBe(400);
    expect(response.body.error.code).toBe('VALIDATION_ERROR');
  });

  test('handles idempotency', async () => {
    const key = 'test-idempotency-key';
    const response1 = await api.post('/v1/orders', data, {
      headers: { 'X-Idempotency-Key': key }
    });
    const response2 = await api.post('/v1/orders', data, {
      headers: { 'X-Idempotency-Key': key }
    });
    expect(response1.body.id).toBe(response2.body.id);
  });
});

Documentation Requirements

Every endpoint must document:
  1. Description: Clear explanation of what the endpoint does
  2. Authentication: Required permissions
  3. Parameters: All query params, headers, and body fields
  4. Response: Success and error response formats
  5. Examples: Working code examples in multiple languages
  6. Rate Limits: Specific limits if different from defaults
  7. Webhooks: Related webhook events
  8. See Also: Links to related endpoints

Security Best Practices

  1. Always use HTTPS
  2. Validate all inputs - Never trust client data
  3. Rate limit all endpoints
  4. Log security events - Failed auth, permission denials
  5. Sanitize outputs - Prevent XSS in responses
  6. Use secure headers:
    X-Content-Type-Options: nosniff
    X-Frame-Options: DENY
    X-XSS-Protection: 1; mode=block
    

Monitoring and Observability

Include correlation IDs in all requests:
X-Request-ID: client-generated-uuid
X-Correlation-ID: server-generated-uuid
Log format:
{
  "timestamp": "2024-01-15T10:30:00Z",
  "request_id": "req_abc123",
  "method": "POST",
  "path": "/v1/orders",
  "status": 201,
  "duration_ms": 145,
  "user_id": "usr_xyz789"
}

Change Management

  1. Backwards Compatibility: Never break existing integrations
  2. Additive Changes: New fields are safe to add
  3. Deprecation Notices: Minimum 90 days
  4. Migration Guides: Provide clear upgrade paths
  5. Changelog: Maintain detailed changelog

Contact

For API design questions or to propose changes to these guidelines: