Skip to content

HTTP Headers Reference

Complete specification of all HTTP headers used in HMAC authentication.

Required Headers

All authenticated requests must include these headers:

Authorization

Contains the HMAC signature and credential information.

Format:

HMAC-SHA256 CredentialType={type}, CredentialId={id}, SignedHeaders={headerList}, Signature={hexSignature}

Example:

HMAC-SHA256 CredentialType=service, CredentialId=external-system-client, SignedHeaders=x-date;x-request-id;x-client-type, Signature=3c92776baecac9a2f88170cc8a9ed1122c7bb92d64eacbcee578c436e7e47a34

Components:

ComponentDescriptionExample
CredentialTypeType of credentialservice or user
CredentialIdCredential identifierexternal-system-client or john@example.com
SignedHeadersList of headers included in signaturex-date;x-request-id;x-client-type
SignatureHMAC-SHA256 signature (hex)3c92776baecac9a2f88170cc...

⚠️ Header Format Rules:

  • Algorithm prefix must be HMAC-SHA256
  • Components separated by commas and spaces
  • CredentialType must be either service or user
  • CredentialId format depends on credential type (slug for service, email for user)
  • Signature must be lowercase hex (64 characters)

X-Date

Request timestamp in ISO 8601 format (UTC timezone).

Format: YYYY-MM-DDTHH:mm:ssZ

Example: 2025-10-14T03:52:12Z

Rules:

  • Must be in UTC (suffix with Z)
  • Must be within ±5 minutes of server time
  • Used in signature calculation and replay protection

Valid formats:

http
✅ X-Date: 2025-10-14T03:52:12Z
✅ X-Date: 2025-10-14T03:52:12.000Z
❌ X-Date: 2025-10-14T10:52:12+07:00  (not UTC)
❌ X-Date: 2025-10-14 03:52:12        (wrong format)

Generation examples:

TypeScript:

typescript
const xDate = new Date().toISOString();
// Output: 2025-10-14T03:52:12.123Z

Python:

python
from datetime import datetime, timezone

x_date = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
# Output: 2025-10-14T03:52:12Z

Go:

go
import "time"

xDate := time.Now().UTC().Format(time.RFC3339)
// Output: 2025-10-14T03:52:12Z

X-Request-Id

Unique identifier for request tracing and debugging.

Format: UUID v4 (lowercase with hyphens)

Example: bf6c5652-9656-4167-b1d4-10c690a72102

Rules:

  • Must be unique for each request
  • Must be valid UUID v4 format
  • Used for request tracking and debugging

Valid formats:

http
✅ X-Request-Id: bf6c5652-9656-4167-b1d4-10c690a72102
❌ X-Request-Id: BF6C5652-9656-4167-B1D4-10C690A72102  (uppercase)
❌ X-Request-Id: bf6c56529656416b1d410c690a72102     (no hyphens)
❌ X-Request-Id: 123456                              (not UUID)

Generation examples:

TypeScript:

typescript
import crypto from 'crypto';

const xRequestId = crypto.randomUUID();
// Output: bf6c5652-9656-4167-b1d4-10c690a72102

Python:

python
from uuid import uuid4

x_request_id = str(uuid4())
# Output: bf6c5652-9656-4167-b1d4-10c690a72102

Go:

go
import "github.com/google/uuid"

xRequestId := uuid.New().String()
// Output: bf6c5652-9656-4167-b1d4-10c690a72102

X-Client-Type

Indicates the type of client making the request.

Format: String enum

Allowed values:

ValueDescriptionUse Case
serviceBackend service/APISystem-to-system integration
webWeb browserWeb application frontend
mobileMobile appiOS/Android applications
cliCommand-line toolScripts and CLI tools
workerBackground workerAsync jobs and scheduled tasks

Example: service

Rules:

  • Must be one of the allowed values
  • Case-sensitive (lowercase only)
  • Required for all requests

Valid examples:

http
✅ X-Client-Type: service
✅ X-Client-Type: web
✅ X-Client-Type: mobile
❌ X-Client-Type: Service    (wrong case)
❌ X-Client-Type: api        (not allowed)
❌ X-Client-Type: custom     (not allowed)

Conditional Headers

These headers are required only in specific scenarios:

X-Tenant-Id

Tenant identifier for multi-tenant requests.

Format: String (alphanumeric, hyphens, underscores)

Example: tenantA or isp-branch-001

Required when:

  • Using user credentials (CredentialType=user)
  • Multi-tenant requests

Not required when:

  • Using service credentials (CredentialType=service)

Valid examples:

http
✅ X-Tenant-Id: tenantA
✅ X-Tenant-Id: isp-branch-001
✅ X-Tenant-Id: customer_123
❌ X-Tenant-Id: tenant@123      (special chars)
❌ X-Tenant-Id: tenant 123      (spaces)

Usage example:

typescript
// Service credential (X-Tenant-Id not needed)
const serviceHeaders = {
  'Authorization': 'HMAC-SHA256 CredentialType=service, CredentialId=external-system-client, SignedHeaders=x-date;x-request-id;x-client-type, Signature=...',
  'X-Date': '2025-10-14T03:52:12Z',
  'X-Request-Id': 'bf6c5652-9656-4167-b1d4-10c690a72102',
  'X-Client-Type': 'service'
};

// User credential (X-Tenant-Id required)
const userHeaders = {
  'Authorization': 'HMAC-SHA256 CredentialType=user, CredentialId=john@example.com, SignedHeaders=x-date;x-tenant-id;x-request-id;x-client-type, Signature=...',
  'X-Date': '2025-10-14T03:52:12Z',
  'X-Request-Id': 'bf6c5652-9656-4167-b1d4-10c690a72102',
  'X-Client-Type': 'web',
  'X-Tenant-Id': 'tenantA'  // ✅ Required for user credentials
};

Standard HTTP Headers

These standard headers should also be included:

Content-Type

Specifies the media type of the request body.

Format: MIME type

Common values:

ValueUsage
application/jsonJSON data (most common)
application/x-www-form-urlencodedForm data
multipart/form-dataFile uploads
text/plainPlain text

Example: application/json

Rules:

  • Required for requests with body (POST, PUT, PATCH)
  • Not required for GET, DELETE requests

Host

Target server hostname.

Format: hostname or hostname:port

Example: olt.remala.local

Rules:

  • Automatically set by HTTP client
  • Must match server hostname
  • Should not be manually set unless necessary

Header Validation Rules

Case Sensitivity

⚠️ Important: Header names in canonical request must be lowercase, but actual HTTP headers can be any case.

typescript
// In canonical request (for signature calculation)
'x-date:2025-10-14T03:52:12Z'      // ✅ Must be lowercase

// In actual HTTP request (both work)
'X-Date: 2025-10-14T03:52:12Z'     // ✅ OK
'x-date: 2025-10-14T03:52:12Z'     // ✅ OK

Signed Headers Order

Headers in SignedHeaders parameter must follow the exact order required by server:

http
✅ SignedHeaders=x-date;x-request-id;x-client-type
❌ SignedHeaders=x-client-type;x-date;x-request-id

For user credentials (with X-Tenant-Id):

http
✅ SignedHeaders=x-date;x-tenant-id;x-request-id;x-client-type
❌ SignedHeaders=x-client-type;x-date;x-request-id;x-tenant-id

Header Values

  • No leading/trailing whitespace
  • No newlines in values
  • UTF-8 encoding
  • Values are case-sensitive

Complete Request Example

Service Authentication Example

http
POST /api/devices HTTP/1.1
Host: olt.remala.local
Authorization: HMAC-SHA256 CredentialType=service, CredentialId=external-system-client, SignedHeaders=x-date;x-request-id;x-client-type, Signature=3c92776baecac9a2f88170cc8a9ed1122c7bb92d64eacbcee578c436e7e47a34
X-Date: 2025-10-14T03:52:12Z
X-Request-Id: bf6c5652-9656-4167-b1d4-10c690a72102
X-Client-Type: service
Content-Type: application/json

{"device_id": "OLT-001", "action": "reboot"}

User Authentication Example

http
GET /api/onu/unconfigured HTTP/1.1
Host: olt.remala.local
Authorization: HMAC-SHA256 CredentialType=user, CredentialId=admin@telkom.com, SignedHeaders=x-date;x-tenant-id;x-request-id;x-client-type, Signature=5d83ac7f3bd1e8a9c2f77e1a9c8e6f4b2d91c5a8e3f7d6b4a2c9e8f1d5a7c3b9
X-Date: 2025-10-14T03:52:12Z
X-Request-Id: af7d4523-8765-4278-a2e5-21d801b83456
X-Client-Type: web
X-Tenant-Id: isp-telkom

Header Troubleshooting

Missing Headers Error

Error Response:

json
{
  "error": "unauthorized",
  "message": "Missing required headers: X-Date, X-Request-Id",
  "code": 401
}

Solution: Ensure all required headers are present in the request.

Check:

  • Authorization header exists
  • X-Date header exists
  • X-Request-Id header exists
  • X-Client-Type header exists
  • X-Tenant-Id exists (if using user credentials)

Invalid Header Format

Error Response:

json
{
  "error": "bad_request",
  "message": "Invalid X-Date format. Expected ISO 8601 (UTC)",
  "code": 400
}

Solution: Check header format matches specification.

Common issues:

  • X-Date not in ISO 8601 format
  • X-Request-Id not valid UUID v4
  • X-Client-Type not one of allowed values
  • Authorization header missing components (CredentialType, CredentialId, SignedHeaders, Signature)

Invalid Authorization Format

Error Response:

json
{
  "error": "unauthorized",
  "message": "Invalid Authorization format",
  "code": 401
}

Solution: Check Authorization header structure.

Common mistakes:

http
❌ Authorization: HMAC-SHA256 Credential=service:external-system-client, ...
   (Using old format Credential=type:id instead of CredentialType, CredentialId)

✅ Authorization: HMAC-SHA256 CredentialType=service, CredentialId=external-system-client, ...
   (Correct format with separate CredentialType and CredentialId)

Timestamp Out of Window

Error Response:

json
{
  "error": "unauthorized",
  "message": "Request timestamp expired. Max allowed: 5 minutes",
  "code": 401
}

Solution:

  1. Synchronize system clock with NTP
  2. Generate timestamp immediately before request
  3. Check network latency
  4. Verify timezone is UTC

Debug steps:

typescript
// Log timestamp being sent
console.log('Sending timestamp:', new Date().toISOString());

// Check server time difference
const serverTime = response.headers['date'];
const clientTime = new Date().toISOString();
console.log('Time diff:', new Date(serverTime) - new Date(clientTime));

SignedHeaders Mismatch

Error Response:

json
{
  "error": "unauthorized",
  "message": "Signature verification failed",
  "code": 401
}

Common causes:

  • Headers in SignedHeaders not in required server order
  • Header included in SignedHeaders but missing from request
  • Header name case mismatch in canonical request

Solution:

typescript
// ✅ Correct (server order)
SignedHeaders=x-date;x-request-id;x-client-type

// ❌ Wrong order
SignedHeaders=x-client-type;x-date;x-request-id

// ❌ Missing header
SignedHeaders=x-date;x-tenant-id;x-request-id;x-client-type
// But X-Tenant-Id not sent in actual request

Quick Reference Table

HeaderRequiredFormatExample
Authorization✅ AlwaysHMAC-SHA256 formatHMAC-SHA256 CredentialType=service, CredentialId=external-system-client, ...
X-Date✅ AlwaysISO 8601 (UTC)2025-10-14T03:52:12Z
X-Request-Id✅ AlwaysUUID v4bf6c5652-9656-4167-b1d4-10c690a72102
X-Client-Type✅ AlwaysEnumservice, web, mobile, cli, worker
X-Tenant-Id⚠️ User credentials onlyAlphanumerictenantA
Content-Type⚠️ With bodyMIME typeapplication/json
Host✅ AlwaysHostnameolt.remala.local

Authorization Header Formats

Service Credentials

HMAC-SHA256 CredentialType=service, CredentialId={service-slug}, SignedHeaders={headers}, Signature={signature}

Example:

HMAC-SHA256 CredentialType=service, CredentialId=external-system-client, SignedHeaders=x-date;x-request-id;x-client-type, Signature=3c92776b...

SignedHeaders for service:

  • Always: x-date;x-request-id;x-client-type

User Credentials

HMAC-SHA256 CredentialType=user, CredentialId={user-email}, SignedHeaders={headers}, Signature={signature}

Example:

HMAC-SHA256 CredentialType=user, CredentialId=admin@telkom.com, SignedHeaders=x-date;x-tenant-id;x-request-id;x-client-type, Signature=5d83ac7f...

SignedHeaders for user:

  • Always: x-date;x-tenant-id;x-request-id;x-client-type

Additional required header:

  • X-Tenant-Id must be present in the request

Best Practices

  1. Generate timestamps immediately before sending - Minimize time drift
  2. Generate a new X-Request-Id for every retry - Prevent replay rejection
  3. Use lowercase header names in canonical request - Prevents signature mismatch
  4. Use exact SignedHeaders order from spec - Required for signature validation
  5. Don't include Authorization in SignedHeaders - It's not part of canonical request
  6. Validate header formats before signing - Catch errors early
  7. Use new format (CredentialType + CredentialId) - Legacy format (Credential=type:id) is deprecated

Next Steps