REST API Reference
Complete reference for Stack9's auto-generated REST API endpoints. Every entity you define automatically gets a full CRUD REST API with filtering, pagination, sorting, and relationship loading.
Base URL
http://localhost:3000/api
Production:
https://your-app.stack9.cloud/api
Authentication
All API requests require authentication via Bearer token:
Authorization: Bearer YOUR_ACCESS_TOKEN
Get Access Token
POST /auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "your_password"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"email": "user@example.com",
"name": "John Doe"
}
}
Entity Endpoints
For each entity (e.g., customer), Stack9 auto-generates these endpoints:
| Method | Endpoint | Description |
|---|---|---|
GET | /api/{entity} | List all records |
GET | /api/{entity}/:id | Get single record |
POST | /api/{entity} | Create new record |
PUT | /api/{entity}/:id | Update record |
DELETE | /api/{entity}/:id | Delete record |
Examples
List All Records
GET /api/customer
Authorization: Bearer YOUR_TOKEN
Response:
{
"data": [
{
"id": 1,
"email": "john@example.com",
"first_name": "John",
"last_name": "Doe",
"status": "active",
"_created_at": "2025-01-10T10:30:00Z",
"_updated_at": "2025-01-10T10:30:00Z"
},
{
"id": 2,
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Smith",
"status": "trial",
"_created_at": "2025-01-09T15:20:00Z",
"_updated_at": "2025-01-09T15:20:00Z"
}
],
"pagination": {
"page": 1,
"per_page": 20,
"total": 2,
"total_pages": 1
}
}
Get Single Record
GET /api/customer/1
Authorization: Bearer YOUR_TOKEN
Response:
{
"id": 1,
"email": "john@example.com",
"first_name": "John",
"last_name": "Doe",
"status": "active",
"company": "Acme Inc",
"phone": "+1234567890",
"_created_at": "2025-01-10T10:30:00Z",
"_updated_at": "2025-01-10T10:30:00Z",
"_created_by": 5,
"_updated_by": 5
}
Create Record
POST /api/customer
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
{
"email": "newcustomer@example.com",
"first_name": "Alice",
"last_name": "Johnson",
"status": "trial",
"company": "StartupCo"
}
Response:
{
"id": 3,
"email": "newcustomer@example.com",
"first_name": "Alice",
"last_name": "Johnson",
"status": "trial",
"company": "StartupCo",
"_created_at": "2025-01-11T08:15:00Z",
"_updated_at": "2025-01-11T08:15:00Z",
"_created_by": 5,
"_updated_by": 5
}
Update Record
PUT /api/customer/3
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
{
"status": "active",
"phone": "+1987654321"
}
Response:
{
"id": 3,
"email": "newcustomer@example.com",
"first_name": "Alice",
"last_name": "Johnson",
"status": "active",
"company": "StartupCo",
"phone": "+1987654321",
"_created_at": "2025-01-11T08:15:00Z",
"_updated_at": "2025-01-11T09:22:00Z",
"_created_by": 5,
"_updated_by": 5
}
Delete Record
DELETE /api/customer/3
Authorization: Bearer YOUR_TOKEN
Response:
{
"success": true,
"message": "Record deleted successfully"
}
Query Parameters
Filtering
Filter records using query parameters:
GET /api/customer?status=active
GET /api/customer?status=active&company=Acme Inc
Operators:
| Operator | Syntax | Example |
|---|---|---|
| Equals | field=value | status=active |
| Not equals | field__ne=value | status__ne=inactive |
| Greater than | field__gt=value | price__gt=100 |
| Less than | field__lt=value | price__lt=500 |
| Greater or equal | field__gte=value | age__gte=18 |
| Less or equal | field__lte=value | age__lte=65 |
| Like (contains) | field__like=value | name__like=John |
| In array | field__in=val1,val2 | status__in=active,trial |
| Not in array | field__nin=val1,val2 | status__nin=cancelled |
| Is null | field__null=true | deleted_at__null=true |
| Is not null | field__null=false | email__null=false |
Examples:
# Customers with active status
GET /api/customer?status=active
# Orders with total greater than $100
GET /api/order?total__gt=100
# Products with price between $50 and $200
GET /api/product?price__gte=50&price__lte=200
# Customers whose name contains "John"
GET /api/customer?first_name__like=John
# Orders with status pending or processing
GET /api/order?status__in=pending,processing
# Products not deleted (deleted_at is null)
GET /api/product?deleted_at__null=true
Pagination
Control pagination with page and per_page parameters:
GET /api/customer?page=1&per_page=20
Parameters:
| Parameter | Default | Max | Description |
|---|---|---|---|
page | 1 | - | Page number (starts at 1) |
per_page | 20 | 100 | Records per page |
Response includes pagination metadata:
{
"data": [...],
"pagination": {
"page": 2,
"per_page": 20,
"total": 150,
"total_pages": 8
}
}
Sorting
Sort results using sort parameter:
# Sort by single field (ascending)
GET /api/customer?sort=first_name
# Sort descending (prefix with -)
GET /api/customer?sort=-created_at
# Sort by multiple fields
GET /api/customer?sort=status,-created_at
Examples:
# Newest customers first
GET /api/customer?sort=-_created_at
# Active customers, alphabetically
GET /api/customer?status=active&sort=first_name
# Orders by total (highest first), then by date
GET /api/order?sort=-total,-created_at
Field Selection
Select specific fields using fields parameter:
# Only return id, email, and status
GET /api/customer?fields=id,email,status
Response:
{
"data": [
{
"id": 1,
"email": "john@example.com",
"status": "active"
},
{
"id": 2,
"email": "jane@example.com",
"status": "trial"
}
],
"pagination": {...}
}
Including Relationships
Load related entities using include parameter:
# Load customer's orders
GET /api/customer/1?include=orders
# Load multiple relationships
GET /api/order/1?include=customer,order_items
# Nested relationships
GET /api/order/1?include=order_items.product
Response with relationships:
{
"id": 1,
"order_number": "ORD-20250110-0001",
"total": 150.00,
"customer": {
"id": 5,
"email": "customer@example.com",
"first_name": "John",
"last_name": "Doe"
},
"order_items": [
{
"id": 1,
"quantity": 2,
"unit_price": 50.00,
"product": {
"id": 10,
"name": "Widget",
"sku": "WDG-001"
}
},
{
"id": 2,
"quantity": 1,
"unit_price": 50.00,
"product": {
"id": 11,
"name": "Gadget",
"sku": "GDG-002"
}
}
]
}
Search
Full-text search using search parameter:
# Search customers by name or email
GET /api/customer?search=john
# Search with filters
GET /api/customer?search=john&status=active
The search looks in text fields defined in your entity configuration.
Complex Queries
Combine multiple query parameters:
# Active customers named John, newest first, with orders
GET /api/customer?status=active&first_name__like=John&sort=-_created_at&include=orders
# Orders over $100, pending or processing, with items and customer
GET /api/order?total__gt=100&status__in=pending,processing&include=order_items,customer
# Products in specific categories, in stock, sorted by price
GET /api/product?category_id__in=5,8,12&stock__gt=0&sort=price
Bulk Operations
Bulk Create
POST /api/customer/bulk
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
{
"records": [
{
"email": "customer1@example.com",
"first_name": "Customer",
"last_name": "One"
},
{
"email": "customer2@example.com",
"first_name": "Customer",
"last_name": "Two"
}
]
}
Response:
{
"success": true,
"created": 2,
"ids": [10, 11]
}
Bulk Update
PUT /api/customer/bulk
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
{
"filter": {
"status": "trial"
},
"update": {
"onboarding_email_sent": true
}
}
Response:
{
"success": true,
"updated": 25
}
Bulk Delete
DELETE /api/customer/bulk
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
{
"ids": [10, 11, 12]
}
Response:
{
"success": true,
"deleted": 3
}
Error Responses
400 Bad Request
Invalid request parameters or validation errors:
{
"error": "Validation Error",
"message": "Request validation failed",
"errors": [
{
"field": "email",
"message": "Email is required"
},
{
"field": "status",
"message": "Invalid status value"
}
]
}
401 Unauthorized
Missing or invalid authentication:
{
"error": "Unauthorized",
"message": "Authentication required"
}
403 Forbidden
User doesn't have permission:
{
"error": "Forbidden",
"message": "You don't have permission to access this resource"
}
404 Not Found
Resource doesn't exist:
{
"error": "Not Found",
"message": "Customer with id 999 not found"
}
422 Unprocessable Entity
Entity hook validation failed:
{
"error": "Validation Error",
"message": "Entity validation failed",
"errors": [
"Insufficient stock for Product ABC",
"Invalid status transition from pending to delivered"
]
}
500 Internal Server Error
Server error:
{
"error": "Internal Server Error",
"message": "An unexpected error occurred",
"request_id": "req_abc123"
}
Rate Limiting
API requests are rate limited:
| Limit | Window | Headers |
|---|---|---|
| 1000 requests | per hour | X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset |
Response headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1610123400
Rate limit exceeded (429):
{
"error": "Rate Limit Exceeded",
"message": "Too many requests. Please try again in 15 minutes.",
"retry_after": 900
}
Webhooks
Register webhooks to receive real-time notifications:
POST /api/webhooks
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
{
"url": "https://your-app.com/webhook",
"events": ["customer.created", "customer.updated", "order.created"],
"secret": "your_webhook_secret"
}
Webhook payload:
{
"event": "customer.created",
"entity": "customer",
"data": {
"id": 15,
"email": "new@example.com",
"first_name": "New",
"last_name": "Customer",
"_created_at": "2025-01-11T10:30:00Z"
},
"timestamp": "2025-01-11T10:30:01Z",
"signature": "sha256=abc123..."
}
Verify Webhook Signature
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return `sha256=${expectedSignature}` === signature;
}
Code Examples
JavaScript/TypeScript
// Using fetch
const response = await fetch('https://your-app.stack9.cloud/api/customer', {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
});
const data = await response.json();
// Create customer
const newCustomer = await fetch('https://your-app.stack9.cloud/api/customer', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'new@example.com',
first_name: 'New',
last_name': 'Customer',
}),
});
Python
import requests
# Get customers
response = requests.get(
'https://your-app.stack9.cloud/api/customer',
headers={
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json',
},
params={
'status': 'active',
'page': 1,
'per_page': 20,
}
)
customers = response.json()
# Create customer
response = requests.post(
'https://your-app.stack9.cloud/api/customer',
headers={
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json',
},
json={
'email': 'new@example.com',
'first_name': 'New',
'last_name': 'Customer',
}
)
new_customer = response.json()
cURL
# Get customers
curl -X GET 'https://your-app.stack9.cloud/api/customer?status=active' \
-H 'Authorization: Bearer YOUR_TOKEN'
# Create customer
curl -X POST 'https://your-app.stack9.cloud/api/customer' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"email": "new@example.com",
"first_name": "New",
"last_name": "Customer"
}'
# Update customer
curl -X PUT 'https://your-app.stack9.cloud/api/customer/1' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"status": "active"
}'
# Delete customer
curl -X DELETE 'https://your-app.stack9.cloud/api/customer/1' \
-H 'Authorization: Bearer YOUR_TOKEN'
Best Practices
- Use field selection - Only request fields you need
- Paginate large datasets - Don't fetch all records at once
- Cache responses - Reduce API calls with client-side caching
- Handle errors gracefully - Implement retry logic for transient failures
- Respect rate limits - Implement backoff strategies
- Use bulk operations - More efficient for multiple records
- Include relationships wisely - Only load what you need
- Validate on client - Reduce validation errors
- Use webhooks - Get real-time updates instead of polling
- Monitor usage - Track API calls and performance
Next Steps
- Query Syntax - Advanced query patterns
- Entity Configuration - Entity field types and options
- SDK Reference - TypeScript SDK for action types and hooks