docsGetting StartedAuthentication

Authentication

Lorn AI uses API keys to authenticate requests. This guide covers how to obtain, use, and secure your credentials.


API Keys

Obtaining Your API Key

API keys are provided when you set up your Lorn AI account. Each key is scoped to specific merchants and permissions.

To request an API key:

  1. Contact mayank@lornai.com.com
  2. Specify which merchant catalogs you need access to
  3. Indicate your environment (sandbox or production)

Using Your API Key

Include your API key in the X-ACP-API-Key header with every request:

curl "https://{{YOUR_STORE_URL}}/acp/products" \
  -H "X-ACP-API-Key: {{YOUR_API_KEY}}"

Alternatively, you can use Basic Auth if configured:

curl "https://{{YOUR_STORE_URL}}/acp/products" \
  -H "Authorization: Basic {{BASE64_USER_PASS}}"

Where {{BASE64_USER_PASS}} is the Base64 encoding of username:password.


Request Headers

All Lorn AI endpoints accept these headers:

Required Headers

HeaderDescriptionExample
X-ACP-API-KeyYour API authentication keysk_live_abc123...
HeaderDescriptionExample
AcceptResponse format (always JSON)application/json
Content-TypeRequest body format for POST/PATCHapplication/json
Accept-LanguagePreferred locale for messagesen-US
Idempotency-KeyPrevents duplicate operationsuuid-v4-string
Request-IdUnique request identifier for tracingreq_abc123

Full ACP-Compliant Headers

For full ACP compliance (when integrating with ChatGPT directly), include:

HeaderDescriptionExample
AuthorizationBearer token or API keyBearer api_key_123
Accept-LanguagePreferred localeen-US
User-AgentClient informationChatGPT/2.0 (Mac OS X 15.0.1)
Idempotency-KeyIdempotency identifieridem_abc123
Request-IdRequest trace IDreq_xyz789
Content-TypeBody content typeapplication/json
SignatureBase64 encoded request signatureeyJtZX...
TimestampRFC 3339 timestamp2025-01-15T10:30:00Z
API-VersionAPI version2025-01-01

Request Signing

For enhanced security, requests can be signed using HMAC-SHA256.

Generating a Signature

import hmac
import hashlib
import base64
import json
from datetime import datetime, timezone
 
def sign_request(body: dict, secret: str) -> tuple[str, str]:
    """Generate signature and timestamp for a request."""
    timestamp = datetime.now(timezone.utc).isoformat()
    body_bytes = json.dumps(body).encode('utf-8')
    
    # Create HMAC-SHA256 signature
    signature = hmac.new(
        secret.encode('utf-8'),
        body_bytes,
        hashlib.sha256
    ).digest()
    
    # Base64 encode
    signature_b64 = base64.b64encode(signature).decode('utf-8')
    
    return signature_b64, timestamp
 
# Usage
body = {"items": [{"product_id": "prod_123", "quantity": 1}]}
signature, timestamp = sign_request(body, "your_signing_secret")
 
headers = {
    "Signature": signature,
    "Timestamp": timestamp,
    "Content-Type": "application/json"
}

Verifying Webhook Signatures

When receiving webhooks, verify the signature to ensure authenticity:

def verify_webhook_signature(
    body: bytes,
    signature_header: str,
    secret: str
) -> bool:
    """Verify a webhook signature."""
    # Parse signature header: "t=<timestamp>,v1=<hex_digest>"
    parts = dict(p.split('=', 1) for p in signature_header.split(','))
    timestamp = parts.get('t')
    received_sig = parts.get('v1')
    
    # Compute expected signature
    expected_sig = hmac.new(
        secret.encode('utf-8'),
        body,
        hashlib.sha256
    ).hexdigest()
    
    # Compare signatures (timing-safe)
    return hmac.compare_digest(expected_sig, received_sig)

Idempotency

Use the Idempotency-Key header to safely retry requests without causing duplicate operations.

How It Works

  1. Include a unique Idempotency-Key with your request
  2. If the request fails and you retry with the same key, you get the same response
  3. Lorn AI stores idempotency keys for 24 hours

Example

# First request
curl -X POST "https://{{YOUR_STORE_URL}}/checkout_sessions" \
  -H "X-ACP-API-Key: {{YOUR_API_KEY}}" \
  -H "Idempotency-Key: order_12345_attempt_1" \
  -H "Content-Type: application/json" \
  -d '{"items": [{"product_id": "prod_123", "quantity": 1}]}'
 
# Retry with same key (returns same response, no duplicate)
curl -X POST "https://{{YOUR_STORE_URL}}/checkout_sessions" \
  -H "X-ACP-API-Key: {{YOUR_API_KEY}}" \
  -H "Idempotency-Key: order_12345_attempt_1" \
  -H "Content-Type: application/json" \
  -d '{"items": [{"product_id": "prod_123", "quantity": 1}]}'

Best Practices

  • Use UUIDs or deterministic keys based on your business logic
  • Include context in the key (e.g., user_123_order_456)
  • Always use idempotency keys for checkout operations

CORS Configuration

Lorn AI supports Cross-Origin Resource Sharing (CORS) for browser-based applications.

Default Configuration

By default, CORS allows all origins (*). For production, configure allowed origins via the ALLOWED_ORIGINS environment variable:

ALLOWED_ORIGINS=https://your-app.com,https://staging.your-app.com

CORS Headers

Lorn AI responds with these CORS headers:

Access-Control-Allow-Origin: https://your-app.com
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: *
Access-Control-Allow-Credentials: true

Rate Limits

To ensure fair usage, Lorn AI applies rate limits:

TierRequests/MinuteRequests/Day
Sandbox601,000
Production600100,000
EnterpriseCustomCustom

Rate Limit Headers

Responses include rate limit information:

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1705312260

Handling Rate Limits

When you exceed limits, you’ll receive a 429 Too Many Requests response:

{
  "error": {
    "type": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Retry after 60 seconds.",
    "retry_after": 60
  }
}

Implement exponential backoff in your client:

import time
import requests
 
def make_request_with_retry(url, headers, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)
        
        if response.status_code == 429:
            retry_after = int(response.headers.get('Retry-After', 60))
            time.sleep(retry_after * (2 ** attempt))  # Exponential backoff
            continue
            
        return response
    
    raise Exception("Max retries exceeded")

Security Best Practices

Protect Your API Keys

  1. Never commit keys to version control

    # Add to .gitignore
    .env
    *.key
  2. Use environment variables

    import os
    API_KEY = os.getenv("LORN_API_KEY")
  3. Rotate keys periodically

    • Request new keys quarterly
    • Revoke old keys after confirming new ones work

Secure Your Requests

  1. Always use HTTPS — Never send credentials over HTTP
  2. Validate SSL certificates — Don’t disable certificate verification
  3. Use request signing — For sensitive operations
  4. Implement idempotency — For all mutating operations

Monitor for Abuse

  1. Log all API requests with timestamps and client identifiers
  2. Set up alerts for unusual activity patterns
  3. Regularly audit API key usage

Environment-Specific Keys

Use different API keys for each environment:

EnvironmentKey PrefixUsage
Developmentsk_dev_Local testing
Sandboxsk_sandbox_Integration testing
Productionsk_live_Live traffic
import os
 
ENV = os.getenv("ENVIRONMENT", "development")
 
API_KEYS = {
    "development": os.getenv("LORN_DEV_KEY"),
    "sandbox": os.getenv("LORN_SANDBOX_KEY"),
    "production": os.getenv("LORN_LIVE_KEY"),
}
 
API_KEY = API_KEYS[ENV]

Troubleshooting

”Invalid API key” Error

{
  "error": {
    "type": "authentication_error",
    "message": "Invalid API key provided"
  }
}

Solutions:

  1. Verify the key is correct (no extra whitespace)
  2. Check the key hasn’t been revoked
  3. Ensure you’re using the right key for the environment

”Unauthorized” Error

{
  "error": {
    "type": "authorization_error",
    "message": "API key does not have access to this resource"
  }
}

Solutions:

  1. Verify your key has access to the requested merchant
  2. Check if the endpoint requires elevated permissions
  3. Contact support to update key permissions

Signature Verification Failed

If request signing is enabled and verification fails:

  1. Ensure the timestamp is within the allowed window (±5 minutes)
  2. Verify the signing secret matches
  3. Check that the request body hasn’t been modified after signing