The unified analytics and event ingestion API for the Nonito Platform. Track user behavior, manage customer profiles, send messaging templates, and build personalized experiences at scale.
Fast & Async
Asynchronous processing ensures fast response times regardless of database latency.
Easy Integration
Simple REST API works with any language or framework. Get started in minutes.
Secure by Default
Bearer token authentication with legacy API key fallback. Only authorized apps access your data.
Rich User Profiles
Capture default and custom attributes to build complete customer profiles.
Quick Start
Get up and running with the Nonito API in three simple steps.
Get your API Key
API keys are available in the Nonito dashboard.
Include the Authorization header
Add the Authorization: Bearer <token> header to all your requests.
Start tracking
Use /events to track behavior and /user-attributes to manage profiles.
Try it out
curl -X POST https://api.nonito.xyz/events \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-api-key" \ -d '{"event_id": "550e8400-e29b-41d4-a716-446655440000", "event_name": "page_view", "event_params": {"page": "/home"}}'
Events Endpoint
Track analytics events with automatic deduplication. Processing is asynchronous — the server responds immediately.
https://api.nonito.xyz/eventsIngests a single frontend analytics event. Processing is asynchronous — the server responds immediately and stores the event in a background thread. Deduplication is handled by event_id.
Request Body
| Parameter | Type | Description |
|---|---|---|
event_idRequired | string | Unique identifier for the event. Used for deduplication — generate a UUID on the client side. |
event_nameRequired | string | The name/type of the event (e.g., "page_view", "button_click", "purchase") |
event_paramsOptional | object | Arbitrary JSON object containing event metadata |
User Attributes
Set or update user profile attributes with upsert behavior.
https://api.nonito.xyz/user-attributesCreate or update user profile attributes. New users are created, existing users are updated. Default attributes are overwritten; custom attributes are merged — set a value to null to remove it.
Request Body
| Parameter | Type | Description |
|---|---|---|
user_idRequired | string | Unique identifier for the user |
attributesOptional | object | Object containing user attributes to set |
Default Attributes
These attributes are stored as dedicated columns:
| Attribute | Type | Description |
|---|---|---|
first_name | string | User's first name |
last_name | string | User's last name |
email | string | User's email address |
phone | string | User's phone number |
gender | string | User's gender (m, f, o, u, n, p) |
date_of_birth | string | ISO 8601 date (YYYY-MM-DD) |
country | string | User's country |
language | string | Preferred language |
home_city | string | User's home city |
email_subscription_state | string | opted_in, subscribed, or unsubscribed |
push_subscription_state | string | opted_in, subscribed, or unsubscribed |
Custom Attributes
Any attribute not in the default list is stored as a custom attribute. Custom attributes support strings, numbers, booleans, dates (ISO 8601), arrays, and nested objects. Pass null to remove a custom attribute.
Try it out
curl -X POST https://api.nonito.xyz/user-attributes \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-api-key" \ -d '{"user_id": "user_123", "attributes": {"first_name": "Alex", "email": "alex@example.com", "vip_level": 5}}'
Send Template
Send a WhatsApp or messaging template to a recipient.
https://api.nonito.xyz/sendtemplateSend a messaging template to a recipient phone number. Provide your API key, the target phone number, the template name, and the template text content.
Request Body
| Parameter | Type | Description |
|---|---|---|
api_keyRequired | string | Your API key for authentication |
phone_numberRequired | string | The recipient's phone number in international format (e.g., "+1234567890") |
template_nameRequired | string | The name of the template to send (e.g., "welcome_message", "order_confirmation") |
template_textRequired | string | The text content of the template |
Try it out
curl -X POST https://api.nonito.xyz/sendtemplate \ -H "Content-Type: application/json" \ -d '{"api_key": "your-api-key", "phone_number": "+1234567890", "template_name": "welcome_message", "template_text": "Hello {{name}}, welcome to our platform!"}'
Batch Event Ingestion
Ingest batches of strictly validated transactional events (orders, customers). Processing is synchronous — the full batch is validated and persisted before responding.
https://api.nonito.xyz/v1/eventsEach event's properties are validated against Pydantic schemas matching the event_name. Persisted via the ingest_event_batch Supabase RPC function. A request_id is returned for tracing.
For automatic Odoo source tagging, use the /v1/odoo/events variant which injects "source": "odoo" into every event.
Request Body
{
"batch": [
{
"event_id": "string (required — unique, for deduplication)",
"user_id": "string (required — E.164 phone number)",
"event_name": "string (required — must be an allowed event type)",
"timestamp": "string (optional — ISO 8601, defaults to now)",
"properties": { }
}
]
}Batch Event Fields
| Field | Type | Description |
|---|---|---|
event_id Required | string | Unique identifier for deduplication |
user_id Required | string | E.164 phone number (e.g., +201234567890) |
event_name Required | string | Must be an allowed event type (see Event Types) |
timestamp Optional | string | ISO 8601 timestamp. Defaults to current time |
properties Required | object | Event-specific data, validated against schema for the event type |
Response (202 Accepted)
{
"status": "queued",
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"message": "Successfully queued 1 events."
}Example
curl -X POST https://api.nonito.xyz/v1/events \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-api-key" \ -d '{"batch": [{"event_id": "ord_001", "user_id": "+201234567890", "event_name": "order_created", "timestamp": "2025-01-15T10:30:00Z", "properties": {"order_id": "ORD-1001", "channel": "online", "financials": {"subtotal": 250, "total_price": 256.5, "currency": "EGP", "payment_method": "visa"}, "customer": {"first_name": "Ahmed", "last_name": "Hassan", "phone": "+201234567890"}, "items": [{"product_id": "SKU-100", "name": "Classic T-Shirt", "quantity": 2, "unit_price": 125, "total_price": 250}]}}]}'
Allowed Event Types
Each event type maps to a specific Pydantic validation schema for the properties object.
| Event Name | Description |
|---|---|
order_created | A new order was placed |
order_updated | An existing order was modified |
order_cancelled | An order was cancelled |
order_packed | An order was packed for shipping |
order_dispatched | An order was dispatched |
order_delivered | An order was delivered |
customer_created | A new customer was registered |
customer_updated | Customer info was updated |
Validation Schemas
Each event type's properties object is validated against these Pydantic models. Invalid properties return a 400 with detailed per-field errors.
order_created
| Field | Type | Description |
|---|---|---|
order_id Required | string | Unique order identifier |
channel Required | string | "online" or "store" |
financials Required | Financials | Payment and pricing details |
branch Conditional | BranchInfo | Required when channel is "store" |
customer Required | CustomerInfo | Customer details |
items Required | OrderItem[] | List of line items |
note Optional | string | Order note |
order_updated
| Field | Type | Description |
|---|---|---|
order_id Required | string | Order identifier |
status Required | string | New order status |
updated_at Optional | string | ISO 8601 timestamp |
note Optional | string | Update note |
financials Optional | Financials | Updated financials |
items Optional | OrderItem[] | Updated line items |
customer Optional | CustomerInfo | Updated customer info |
branch Optional | BranchInfo | Updated branch info |
order_cancelled
| Field | Type | Description |
|---|---|---|
order_id Required | string | Order identifier |
reason Required | string | Cancellation reason |
refunded_amount Optional | float | Amount refunded (default 0.0) |
reference_number Optional | string | Refund transaction ID |
order_packed / order_dispatched
| Field | Type | Description |
|---|---|---|
order_id Required | string | Order identifier |
delivery_company Required | string | Shipping carrier |
tracking_number Required | string | Tracking number |
tracking_link Optional | string | Tracking URL |
note Optional | string | Packing/dispatch note |
order_delivered
| Field | Type | Description |
|---|---|---|
order_id Required | string | Order identifier |
delivery_company Required | string | Shipping carrier (e.g., Bosta, Aramex) |
tracking_number Required | string | Tracking number |
tracking_link Optional | string | Tracking URL |
delivery_agent Optional | string | Delivery agent name |
received_by Optional | string | Person who received the order |
customer_created / customer_updated
| Field | Type | Description |
|---|---|---|
first_name Required | string | Customer first name |
last_name Required | string | Customer last name |
email Optional | string | Customer email |
phone Required | string | Customer phone (E.164) |
marketing_consent Optional | boolean | Marketing opt-in (default false) |
Validation Error Format
{
"error": "Validation Failed",
"message": "One or more events in the batch are invalid.",
"details": [
{
"index": 0,
"errors": [
"Missing required field: 'order_id'",
"financials.total_price: field required"
]
}
]
}Nested Models
Shared data models referenced by the validation schemas above.
Financials
| Field | Type | Default | Description |
|---|---|---|---|
subtotal Required | float | — | Pre-discount subtotal |
total_discounts Optional | float | 0.0 | Total discounts applied |
total_tax Optional | float | 0.0 | Total tax |
total_price Required | float | — | Final price |
currency Optional | string | "EGP" | Currency code |
payment_method Required | string | — | e.g., cash, visa, wallet |
promo_codes Optional | string[] | — | Coupons applied |
BranchInfo
| Field | Type | Description |
|---|---|---|
branch_id Required | string | Branch identifier |
name Required | string | Branch name |
location Optional | string | Physical address or coordinates |
city Optional | string | City / Governorate |
phone Optional | string | Branch contact number |
CustomerInfo
| Field | Type | Description |
|---|---|---|
first_name Required | string | First name |
last_name Required | string | Last name |
email Optional | string | Email address |
phone Required | string | Phone number |
OrderItem
| Field | Type | Default | Description |
|---|---|---|---|
product_id Required | string | — | Product identifier / SKU |
name Required | string | — | Product name |
quantity Required | integer | — | Quantity ordered |
unit_price Required | float | — | Price per unit |
total_discount Optional | float | 0.0 | Discount on this item |
total_tax Optional | float | 0.0 | Tax on this item |
total_price Required | float | — | Final line item price |
category Optional | CategoryInfo | — | Product category |
CategoryInfo
| Field | Type | Description |
|---|---|---|
id Required | string | Category identifier |
name Required | string | Category name |
Authentication
All endpoints require authentication via API key.
API keys are validated against the nonito_api_keys table in Supabase. A valid key resolves to a nonito_id (tenant identifier).
Supported Auth Methods
| Method | Header | Priority |
|---|---|---|
| Bearer Token (standard) | Authorization: Bearer <token> | Primary |
| Legacy | X-API-Key: <token> | Fallback |
Validation Flow
Extract token
From Authorization: Bearer header (primary) or X-API-Key header (fallback).
Query database
Match token against the api_key column in nonito_api_keys table.
Authenticate
If found, the nonito_id is used as the tenant identifier. If not, return 401 Unauthorized.
Example
Authorization: Bearer 550e8400-e29b-41d4-a716-446655440000
Code Examples
Example implementations in popular languages.
Events API
cURL
curl -X POST https://api.nonito.xyz/events \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-api-key" \ -d '{ "event_id": "550e8400-e29b-41d4-a716-446655440000", "event_name": "button_click", "event_params": { "user_id": "12345", "button_id": "signup_btn", "page": "/home" } }'
JavaScript / TypeScript
const API_KEY = 'your-api-key'; const BASE_URL = 'https://api.nonito.xyz'; const trackEvent = async (eventName, params = {}) => { const response = await fetch(`${BASE_URL}/events`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${API_KEY}`, }, body: JSON.stringify({ event_id: crypto.randomUUID(), event_name: eventName, event_params: params, }), }); return response.json(); }; // Example usage trackEvent('page_view', { page: '/dashboard', user_id: 'u_123', });
Python
import requests import uuid API_KEY = 'your-api-key' BASE_URL = 'https://api.nonito.xyz' HEADERS = { 'Content-Type': 'application/json', 'Authorization': f'Bearer {API_KEY}', } def track_event(event_name, params=None): return requests.post( f'{BASE_URL}/events', headers=HEADERS, json={ 'event_id': str(uuid.uuid4()), 'event_name': event_name, 'event_params': params or {}, }, ).json() # Example usage track_event("user_signup", { "user_id": "12345", "plan": "premium", "source": "google_ads", })
Swift (iOS)
import Foundation func trackEvent(eventName: String, eventParams: [String: Any]) async throws { let url = URL(string: "https://api.nonito.xyz/events")! var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("Bearer your-api-key", forHTTPHeaderField: "Authorization") let payload: [String: Any] = [ "event_id": UUID().uuidString, "event_name": eventName, "event_params": eventParams ] request.httpBody = try JSONSerialization.data(withJSONObject: payload) let (data, _) = try await URLSession.shared.data(for: request) print(String(data: data, encoding: .utf8) ?? "") }
User Attributes API
cURL
curl -X POST https://api.nonito.xyz/user-attributes \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-api-key" \ -d '{ "user_id": "user_123", "attributes": { "first_name": "Alex", "email": "alex@example.com", "country": "United States", "vip_level": 5, "tags": ["premium", "early_adopter"] } }'
JavaScript / TypeScript
const setUserAttributes = async (userId, attributes) => { const response = await fetch('https://api.nonito.xyz/user-attributes', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-api-key', }, body: JSON.stringify({ user_id: userId, attributes: attributes, }), }); return response.json(); }; // Set default and custom attributes setUserAttributes('user_123', { first_name: 'Alex', email: 'alex@example.com', vip_level: 5, interests: ['tech', 'gaming'], }); // Unset a custom attribute setUserAttributes('user_123', { vip_level: null, // removes the attribute });
Response
API response format and status codes. Legacy endpoints return 202 Accepted to indicate the request was received and queued for async processing.
Events Response
{
"status": "accepted",
"event_id": "550e8400-e29b-41d4-a716-446655440000"
}User Attributes Response
{
"status": "accepted",
"user_id": "user_123"
}Send Template Response
{
"status": "accepted",
"template_name": "welcome_message"
}Status Codes
Standard Error Format
{
"error": "Error type",
"message": "Human-readable description"
}Common Event Examples
Suggested event names and parameters for common use cases.
| Event Name | Suggested Params | Use Case |
|---|---|---|
page_view | page, referrer, user_id | Track page visits |
button_click | button_id, page, user_id | Track UI interactions |
user_signup | user_id, plan, source | Track new registrations |
purchase | user_id, amount, product_id | Track transactions |
app_open | device, version, user_id | Track app launches |
error | message, stack, user_id | Track errors |