Version: 1.0.0
Base URL: https://your-api.railway.app
Authentication: Bearer JWT Token
Format: JSON
---
1. Authentication
2. Core Resources
3. Attribution & Analytics
4. Integrations
5. Email Templates (Super Admin)
6. Error Handling
7. Rate Limits
8. Webhooks
---
POST /auth/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePassword123",
"firstName": "John",
"lastName": "Doe"
}Response:
{
"user": {
"id": "uuid",
"email": "user@example.com",
"firstName": "John",
"lastName": "Doe"
},
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"message": "Registration successful. Please verify your email."
}POST /auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "SecurePassword123"
}Response:
{
"user": {
"id": "uuid",
"email": "user@example.com",
"isSuperAdmin": false
},
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...---
#
GET /brands
Authorization: Bearer {token}Response:
[
{
"id": "brand-uuid",
"name": "My E-commerce Store",
"domain": "mystore.com",
"timezone": "America/New_York",
"currency": "USD",
"pixelId": "px_abc123",
"createdAt": "2025-01-01T00:00:00Z"
}
]#
POST /brands
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "My E-commerce Store",
"domain": "mystore.com",
"timezone": "America/New_York",
"currency": "USD"
}#
PUT /brands/:brandId/settings
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "Updated Store Name",
"timezone": "America/Los_Angeles"
}---
GET /brands/:brandId/attribution?days=30&model=time_decay
Authorization: Bearer {token}Parameters:
days (optional): Number of days to analyze (default: 30)model (optional): Attribution model (time_decay, linear, first_click, last_click, position_based)Response:
{
"totalRevenue": 125430.50,
"totalConversions": 523,
"averageOrderValue": 239.90,
"sourceAttributions": [
{
"source": "facebook",
"medium": "cpc",
"sourceType": "ads",
"touchpoints": 1247,
"conversions": 203,
"revenue": 48730.25,
"roas": 3.24
},
{
"source": "awin",
"medium": "affiliate",
"sourceType": "affiliate",
"touchpoints": 856,
"conversions": 145,
"revenue": 34820.50,
"roas": 4.12
}
],
"dateRange": {
"start": "2025-10-17T00:00:00Z",
"end": "2025-11-16T23:59:59Z"
}
}GET /brands/:brandId/events?page=1&limit=50&type=pageview
Authorization: Bearer {token}Parameters:
page (optional): Page number (default: 1)limit (optional): Items per page (default: 50, max: 100)type (optional): Event type filter (pageview, click, purchase, etc.)source (optional): Filter by traffic sourcestartDate (optional): Filter events after this date (ISO 8601)endDate (optional): Filter events before this date (ISO 8601)Response:
{
"events": [
{
"id": "event-uuid",
"type": "purchase",
"timestamp": "2025-11-16T14:30:00Z",
"sessionId": "session-uuid",
"userId": "user-uuid",
"source": "facebook",
"medium": "cpc",
"campaign": "black-friday-2025",
"revenue": 239.99,
"trackingParams": {
"fbclid": "abc123"
},
"deviceType": "mobile",
"browser": "Chrome",
"os": "iOS"
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 50000,
"totalPages": 1000
}
}GET /brands/:brandId/journeys?userId={userId}
Authorization: Bearer {token}Response:
{
"journeys": [
{
"userId": "user-uuid",
"firstTouch": "2025-11-10T10:00:00Z",
"lastTouch": "2025-11-16T14:30:00Z",
"touchpoints": [
{
"id": "tp-1",
"timestamp": "2025-11-10T10:00:00Z",
"source": "google",
"medium": "organic",
"type": "pageview"
},
{
"id": "tp-2",
"timestamp": "2025-11-12T15:20:00Z",
"source": "klaviyo",
"medium": "email",
"type": "click"
},
{
"id": "tp-3",
"timestamp": "2025-11-16T14:30:00Z",
"source": "facebook",
"medium": "cpc",
"type": "purchase"
}
],
"conversion": {
"timestamp": "2025-11-16T14:30:00Z",
"revenue": 239.99,
"orderId": "order-123"
}
}
]
}GET /reporting/brands/:brandId/overview?days=7
Authorization: Bearer {token}Response:
{
"totalRevenue": 45230.50,
"totalOrders": 189,
"totalEvents": 125840,
"averageOrderValue": 239.31,
"topSources": [
{
"source": "facebook",
"revenue": 18092.20,
"orders": 76,
"percentage": 40
}
],
"revenueOverTime": [
{
"date": "2025-11-10",
"revenue": 6470.50
}
],
"eventsByType": {
"pageview": 98420,
"click": 23140,
"purchase": 189
}
}---
#
GET /integrations/oauth/meta/initiate?brandId={brandId}
Authorization: Bearer {token}Response:
{
"authorizationUrl": "https://www.facebook.com/v21.0/dialog/oauth?client_id=..."
}Flow:
1. Redirect user to authorizationUrl
2. User authorizes
3. Facebook redirects to /integrations/oauth/meta/callback?code=...&state=...
4. System exchanges code for tokens and stores encrypted
#
GET /integrations/:brandId/meta-ads-accounts
Authorization: Bearer {token}Response:
{
"accounts": [
{
"accountId": "act_123456789",
"accountName": "My Ad Account",
"currency": "USD",
"status": "ACTIVE"
}
],
"integrationStatus": "connected",
"lastSync": "2025-11-16T12:00:00Z"
}#
GET /brands/:brandId/settings/custom-sources
Authorization: Bearer {token}Response:
[
{
"id": "source-uuid",
"name": "AWIN Affiliates",
"sourceType": "affiliate",
"identifierParam": "awc",
"defaultUtmSource": "awin",
"defaultUtmMedium": "affiliate",
"costPerConversion": 15.00,
"isActive": true
}
]#
POST /brands/:brandId/settings/custom-sources
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "AWIN Affiliates",
"sourceType": "affiliate",
"identifierParam": "awc",
"defaultUtmSource": "awin",
"defaultUtmMedium": "affiliate",
"costPerConversion": 15.00,
"revenueSharePercent": 10.00,
"notes": "AWIN affiliate network"
}#
GET /brands/:brandId/settings/utm-mappings
Authorization: Bearer {token}Response:
[
{
"id": "mapping-uuid",
"utmSource": "instagram",
"utmMedium": "social",
"utmCampaign": "summer-2025",
"mappedChannel": "Social Media",
"mappedSubchannel": "Instagram - Paid",
"costPerClick": 0.75
}
]---
GET /email-templates
Authorization: Bearer {token}
X-Super-Admin: trueResponse:
[
{
"id": "template-uuid",
"name": "Welcome Email V2",
"templateType": "WELCOME_EMAIL",
"subject": "Welcome to DatomniX! 🎉",
"description": "Modern welcome email with onboarding steps",
"htmlContent": "<!DOCTYPE html>...",
"grapesJsData": {...},
"variables": {
"firstName": "User first name",
"loginUrl": "Login page URL"
},
"isActive": true,
"isDefault": true,
"createdAt": "2025-11-01T00:00:00Z",
"creator": {
"id": "user-uuid",
"email": "admin@datomnix.com"
}
}
]POST /email-templates
Authorization: Bearer {token}
X-Super-Admin: true
Content-Type: application/json
{
"name": "Welcome Email V2",
"templateType": "WELCOME_EMAIL",
"subject": "Welcome to DatomniX! 🎉",
"description": "Modern welcome email",
"htmlContent": "<!DOCTYPE html>...",
"grapesJsData": {...},
"variables": {...},
"isActive": true,
"isDefault": false
}GET /email-templates/variables/available
Authorization: Bearer {token}
X-Super-Admin: trueResponse:
{
"WELCOME_EMAIL": {
"firstName": "User first name",
"lastName": "User last name",
"email": "User email address",
"loginUrl": "Login page URL",
"companyName": "Company name (DatomniX)"
},
"VERIFICATION_EMAIL": {
"firstName": "User first name",
"verificationUrl": "Email verification URL",
"expiryHours": "Link expiry time in hours"
}
}---
POST /webhooks/woocommerce/:brandId/order
Receives order webhooks from a connected WooCommerce store and turns them into canonical events.
Public endpoint: the brand id is in the URL, so **every request that is not a ping must carry a valid
signature**. An unverified payload would be an open door to fabricated orders and inflated revenue.
One webhook per topic, all pointing at the same delivery URL, API version **WP REST API Integration
v3**. WooCommerce generates a fresh secret per webhook, so either paste one secret across all of them
or store each one: the receiver accepts any secret configured for the brand.
| Topic | Why |
|---|---|
| order.created | required |
| order.updated | required. Status transitions, refunds, cancellations, corrected totals |
| order.deleted | recommended, or a removed order keeps earning attribution |
| order.restored | recommended, the other half of order.deleted |
These are deliberately distinct. A single opaque 400 covering all of them once hid a total outage
for a day: every WooCommerce delivery was failing and nothing said so.
| Code | Meaning | What to do |
|---|---|---|
| 200 | Accepted. Also returned for the ping WooCommerce sends when a webhook is saved | nothing |
| 401 | Unsigned, or signed with a secret this brand does not have configured | check the webhook's secret matches the integration |
| 404 | Unknown brand, or the brand has no active WooCommerce integration | check the brand id in the delivery URL |
| 503 | No webhook secret is configured for this integration. Our misconfiguration, not the sender's | configure the secret on the integration |
| 500 | The delivery was authentic and processing it failed | retry; the error is logged against the integration |
WC_Webhook::deliver_ping() posts the form body webhook_id= with no signature header and
requires exactly 200, otherwise WooCommerce refuses to create the webhook. The receiver recognises
it by that shape plus the absence of a signature, so it cannot be used to skip verification: a real
delivery always carries the signature header, and an order payload is never a lone webhook_id.
Read every WooCommerce credential throughapps/api/src/integrations/woocommerce/credentials.ts. **Do not destructure the credentials blob by
hand.** Two call sites did, each expecting field names the blob does not use, and both failed
silently behind a catch: the webhook receiver rejected every delivery, and product enrichment never
ran once, which is why no order carried a real product category.
---
{
"statusCode": 400,
"message": "Validation failed",
"error": "Bad Request",
"details": [
{
"field": "email",
"message": "Email must be a valid email address"
}
]
}200 OK - Success201 Created - Resource created204 No Content - Success with no response body400 Bad Request - Invalid input401 Unauthorized - Missing or invalid authentication403 Forbidden - Insufficient permissions404 Not Found - Resource not found409 Conflict - Resource conflict (e.g., duplicate email)422 Unprocessable Entity - Validation error429 Too Many Requests - Rate limit exceeded500 Internal Server Error - Server error---
Current Limits:
Rate Limit Headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1700000000Rate Limit Exceeded Response:
{
"statusCode": 429,
"message": "Rate limit exceeded. Please try again in 60 seconds.",
"error": "Too Many Requests"
}---
event.created - New tracking event recordedorder.created - New order/conversion trackedintegration.connected - New integration connectedintegration.disconnected - Integration disconnectedattribution.updated - Attribution data recalculated{
"event": "order.created",
"timestamp": "2025-11-16T14:30:00Z",
"brandId": "brand-uuid",
"data": {
"orderId": "order-123",
"revenue": 239.99,
"currency": "USD",
"source": "facebook",
"medium": "cpc",
"campaign": "black-friday-2025"
}
}X-DatomniX-Signature: sha256=abc123...---
Interactive API documentation available at:
https://your-api.railway.app/api-docsFeatures:
---
npm install @datomnix/sdkimport { DatomniXClient } from '@datomnix/sdk'
const client = new DatomniXClient({
apiKey: 'your-api-key',
brandId: 'your-brand-id'
})
// Track event
await client.trackEvent({
type: 'purchase',
revenue: 239.99,
orderId: 'order-123'
})
// Get attribution
const attribution = await client.getAttribution({
days: 30,
model: 'time_decay'
})from datomnix import DatomniXClient
client = DatomniXClient(api_key='your-api-key')
attribution = client.get_attribution(brand_id='brand-id', days=30)---
Documentation: https://docs.datomnix.com
API Status: https://status.datomnix.com
Support Email: support@datomnix.com
Community: https://discord.gg/datomnix
---
API Version: 1.0.0
Last Updated: November 16, 2025