Skip to main content
The payload examples on this page are simplified for readability. Some routes also add rate-limit headers such as X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset.

Endpoint summary

GroupMain endpoints
PublicPOST /api/contact, POST /api/waitlist, GET /api/health
Logged-in userPOST /api/payments, POST /api/subscription, POST /api/profile, POST /api/profile/avatar
AdminPOST /api/admin/users/role
AIPOST /api/ai/chat, POST /api/ai/generate
Provider callbackPOST /api/webhooks/midtrans, POST /api/webhooks/doku

Public endpoints

PropertyValue
AuthNo login required
Feature gateContact feature must be active
Rate limit5 requests per hour per IP
PurposeSend a contact-form message through Resend
Request body:
{
  "name": "Galang",
  "email": "galang@example.com",
  "message": "Hi, I want a demo."
}
Success response:
{
  "success": true
}
Common responses:
StatusWhen it happens
400Required fields are missing or the email is invalid
429Contact rate limit is exceeded
503The contact feature is not active or env is incomplete
500Resend fails to send the email
PropertyValue
AuthNo login required
Feature gateWaitlist feature must be active
Rate limit5 requests per hour per IP
PurposeSave a lead into the waitlist table
Request body:
{
  "email": "galang@example.com",
  "name": "Galang"
}
Success response:
{
  "success": true
}
Common responses:
StatusWhen it happens
400The email is invalid
409The email is already registered
429The waitlist rate limit is exceeded
503The waitlist feature is not active or env is incomplete
500Database insert fails
PropertyValue
AuthNo login required
PurposeCheck feature readiness and database availability
Core response shape:
{
  "status": "ok",
  "payment_provider": "doku",
  "checks": {
    "supabase_public": true,
    "supabase_service_role": true,
    "resend": true
  },
  "database": {
    "ok": true,
    "latency_ms": 24
  }
}
Notes:
  • it returns 200 when active features are healthy,
  • it returns 503 when an active feature is still missing required config or the database check fails,
  • the response also includes a per-feature map with enabled, missing_env, and disabled_by_flag.

Logged-in user endpoints

PropertyValue
AuthLogin required
Feature gatePayments must be active and ready
Rate limit5 requests per 15 minutes per user
PurposeCreate a pending payment and start the provider checkout session
Request body:
{
  "plan": "PRO"
}
Midtrans success response:
{
  "provider": "midtrans",
  "token": "snap-token",
  "orderId": "KK-1742360000000-AB12CD34"
}
Doku success response:
{
  "provider": "doku",
  "payment_url": "https://sandbox.doku.com/...",
  "orderId": "KK-1742360000000-AB12CD34"
}
Common responses:
StatusWhen it happens
400plan is invalid or unavailable
401The user is not logged in
429Payment rate limit is exceeded
503The payments feature is not ready or the provider is not configured
500The payment record cannot be created
PropertyValue
AuthLogin required
PurposeCancel or resume the active subscription
Request body:
{
  "action": "cancel"
}
Valid values:
  • cancel
  • resume
Success response:
{
  "success": true,
  "status": "cancel_at_period_end"
}
Or:
{
  "success": true,
  "status": "active"
}
Common responses:
StatusWhen it happens
400The action value is invalid
401The user is not logged in
503Server-side billing configuration is not ready
PropertyValue
AuthLogin required
PurposeSave full name and avatar references for the current user
Request body:
{
  "full_name": "Galang Pratama",
  "avatar_path": "user-id/avatar",
  "avatar_url": "https://signed-url.example.com/avatar"
}
Success response:
{
  "success": true,
  "profile": {
    "full_name": "Galang Pratama",
    "avatar_path": "user-id/avatar",
    "avatar_url": "https://signed-url.example.com/avatar"
  }
}
Common responses:
StatusWhen it happens
400The profile body is invalid
401The user is not logged in
500The profile update fails
503Server-side write configuration is not ready
PropertyValue
AuthLogin required
Feature gateAuth must be active
PurposeCreate a signed upload URL for avatars
Request body:
{
  "fileSize": 180000,
  "fileType": "image/png"
}
Success response:
{
  "bucket": "avatars",
  "path": "user-id/avatar",
  "token": "signed-upload-token"
}
Common responses:
StatusWhen it happens
400The file is larger than 2 MB or the MIME type is unsupported
401The user is not logged in
500The signed upload URL cannot be created
503The auth feature is not available

Admin endpoint

PropertyValue
AuthLogin required
Authorizationadmin role required
PurposeChange a user’s role to admin or member
Request body:
{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "role": "admin"
}
Success response:
{
  "success": true,
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "role": "admin"
}
Common responses:
StatusWhen it happens
400The body is invalid or an admin tries to demote their own account
401The user is not logged in
403The user is logged in but is not an admin
500The role update fails
503The admin feature is not ready server-side

AI endpoints

PropertyValue
AuthLogin required
Rate limitBased on the user’s plan, per 5 minutes
PurposeStream AI chat responses
Request body:
{
  "provider": "openai",
  "messages": [
    {
      "role": "user",
      "content": "Help me summarize this brief."
    }
  ]
}
Success behavior:
  • returns a UI message stream rather than a plain JSON object,
  • usage is tracked after the stream finishes.
Common responses:
StatusWhen it happens
400messages is missing
401The user is not logged in
429Request rate limit or monthly usage limit is exceeded
503The AI provider is not configured
PropertyValue
AuthLogin required
Rate limitBased on the user’s plan, per 5 minutes
PurposeGenerate one-off text output
Request body:
{
  "provider": "openai",
  "prompt": "Write a short product description",
  "system": "Use a formal tone"
}
Success response:
{
  "text": "Generated output...",
  "usage": {
    "inputTokens": 20,
    "outputTokens": 120
  }
}
Common responses:
StatusWhen it happens
400prompt is missing
401The user is not logged in
429Request rate limit or monthly usage limit is exceeded
503The AI provider is not configured

Provider webhook endpoints

Use this endpoint as a Midtrans callback, not as a browser-facing route.Verified fields:
  • order_id
  • status_code
  • gross_amount
  • signature_key
  • transaction_status
Important behavior:
  • invalid signature returns 401,
  • invalid payload returns 400,
  • missing payment record returns 404,
  • duplicate delivery can return { "received": true, "duplicate": true },
  • valid events update payments, may activate subscriptions, and write into webhook_events and audit_logs.
Use this endpoint as a Doku JOKUL callback, not as a browser-facing route.Verified fields:
  • order.invoice_number
  • order.amount
  • transaction.status
  • transaction.original_request_id
  • security.check_word
Important behavior:
  • invalid signature returns 401,
  • invalid payload returns 400,
  • missing payment record returns 404,
  • duplicate delivery is traceable through webhook_events,
  • valid events update payments, may activate subscriptions, and write an audit trail.
/auth/confirm is an auth callback route, not an /api endpoint. To understand where it fits in login, OTP, and OAuth flows, read End-to-end flows.