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:
- Contact mayank@lornai.com.com
- Specify which merchant catalogs you need access to
- 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
| Header | Description | Example |
|---|---|---|
X-ACP-API-Key | Your API authentication key | sk_live_abc123... |
Recommended Headers
| Header | Description | Example |
|---|---|---|
Accept | Response format (always JSON) | application/json |
Content-Type | Request body format for POST/PATCH | application/json |
Accept-Language | Preferred locale for messages | en-US |
Idempotency-Key | Prevents duplicate operations | uuid-v4-string |
Request-Id | Unique request identifier for tracing | req_abc123 |
Full ACP-Compliant Headers
For full ACP compliance (when integrating with ChatGPT directly), include:
| Header | Description | Example |
|---|---|---|
Authorization | Bearer token or API key | Bearer api_key_123 |
Accept-Language | Preferred locale | en-US |
User-Agent | Client information | ChatGPT/2.0 (Mac OS X 15.0.1) |
Idempotency-Key | Idempotency identifier | idem_abc123 |
Request-Id | Request trace ID | req_xyz789 |
Content-Type | Body content type | application/json |
Signature | Base64 encoded request signature | eyJtZX... |
Timestamp | RFC 3339 timestamp | 2025-01-15T10:30:00Z |
API-Version | API version | 2025-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
- Include a unique
Idempotency-Keywith your request - If the request fails and you retry with the same key, you get the same response
- 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.comCORS 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: trueRate Limits
To ensure fair usage, Lorn AI applies rate limits:
| Tier | Requests/Minute | Requests/Day |
|---|---|---|
| Sandbox | 60 | 1,000 |
| Production | 600 | 100,000 |
| Enterprise | Custom | Custom |
Rate Limit Headers
Responses include rate limit information:
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1705312260Handling 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
-
Never commit keys to version control
# Add to .gitignore .env *.key -
Use environment variables
import os API_KEY = os.getenv("LORN_API_KEY") -
Rotate keys periodically
- Request new keys quarterly
- Revoke old keys after confirming new ones work
Secure Your Requests
- Always use HTTPS — Never send credentials over HTTP
- Validate SSL certificates — Don’t disable certificate verification
- Use request signing — For sensitive operations
- Implement idempotency — For all mutating operations
Monitor for Abuse
- Log all API requests with timestamps and client identifiers
- Set up alerts for unusual activity patterns
- Regularly audit API key usage
Environment-Specific Keys
Use different API keys for each environment:
| Environment | Key Prefix | Usage |
|---|---|---|
| Development | sk_dev_ | Local testing |
| Sandbox | sk_sandbox_ | Integration testing |
| Production | sk_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:
- Verify the key is correct (no extra whitespace)
- Check the key hasn’t been revoked
- 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:
- Verify your key has access to the requested merchant
- Check if the endpoint requires elevated permissions
- Contact support to update key permissions
Signature Verification Failed
If request signing is enabled and verification fails:
- Ensure the timestamp is within the allowed window (±5 minutes)
- Verify the signing secret matches
- Check that the request body hasn’t been modified after signing