Skip to main content

API Overview

Base URL: https://channel.ozzieapp.com/v1

The Channel API is the single integration point for all consumer apps. It is stateful β€” it owns users, OTP authentication, onboarding, chat history, and transactions.

info

The intelligence layer is available at api.ozzieapp.com but is an internal service. Consumer apps β€” including Lovable β€” do not call it directly. All requests go through channel.ozzieapp.com.


Endpoints​

MethodPathDescription
POST/v1/auth/otp/requestRequest a 6-digit OTP code (WhatsApp or email)
POST/v1/auth/otp/verifyVerify OTP code β€” returns user_id + onboarding_stage
POST/v1/usersCreate user identity
GET/v1/users/:user_idGet user
GET/v1/users/:user_id/profileGet channel user profile (onboarding_stage, plan_speed, etc.)
PATCH/v1/users/:user_id/onboarding/completeAdvance onboarding from plan β†’ active
POST/v1/users/:user_id/financial-intakeSubmit financial intake
GET/v1/users/:user_id/financial-intakeGet intake
POST/v1/users/:user_id/plan/generateGenerate plan β€” accepts personality_type in body
GET/v1/users/:user_id/planGet plan
GET/v1/users/:user_id/financial-profileEnvelope allocations β€” accepts ?personality_type=
POST/v1/users/:user_id/chat/completionStateless AI chat inference
GET/v1/users/:user_id/chat/messagesRetrieve chat history
POST/v1/transactions/parseStateless AI transaction parsing
GET/v1/personality/questionsQuiz question catalog (no user_id required)
POST/v1/personality/scoreScore quiz answers and return personality type
POST/v1/money-moves/generateGenerate weekly move recommendations

Authentication​

HTTP Bearer with a base64-encoded client_id:client_secret token.

Token format:

base64(client_id + ":" + client_secret)

Header:

Authorization: Bearer <base64_encoded_credentials>

Generating the token:

const token = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
// Authorization: `Bearer ${token}`
Keep credentials server-side

Never expose your client_secret in client-side code, mobile apps, or public repositories. All API calls must originate from your backend.


Request and response conventions​

Request headers​

HeaderRequiredValue
AuthorizationYesBearer <token>
Content-TypeYes (POST/PATCH)application/json
Idempotency-KeyNoUUID v4 β€” for idempotent POST requests

Response envelope​

{
"object": "object_type_name",
"data": { ... }
}

HTTP status codes​

StatusMeaning
200 OKRequest succeeded
201 CreatedResource was created
400 Bad RequestMalformed JSON or validation error
401 UnauthorizedMissing or invalid credentials
403 ForbiddenValid credentials, insufficient permissions
404 Not FoundResource does not exist
409 ConflictResource already exists
422 Unprocessable EntityBusiness logic error (e.g., intake missing)
429 Too Many RequestsRate limit exceeded
500 Internal Server ErrorServer error

Error codes​

All errors follow this shape:

{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable explanation."
}
}
CodeHTTPDescription
INVALID_JSON400Request body is not valid JSON
VALIDATION_ERROR400Field failed validation
INVALID_CODE400OTP code is wrong or already used
CODE_EXPIRED400OTP code has passed its 10-minute TTL
UNAUTHORIZED401Missing or invalid credentials
FORBIDDEN403Credentials valid but access denied
NOT_FOUND404Resource does not exist
CONFLICT409Unique constraint conflict (e.g., duplicate external_user_id)
INTAKE_REQUIRED422Financial intake must be submitted first
PLAN_REQUIRED422Plan must be generated first
RATE_LIMIT_EXCEEDED429Rate limit exceeded β€” see Retry-After header
INTERNAL_ERROR500Unexpected server error

Rate limiting​

Rate limits are applied per client_id. When exceeded, the API returns HTTP 429:

HTTP/1.1 429 Too Many Requests
Retry-After: 15
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1746460800
TierRequests per minute
Standard120
Growth600
EnterpriseCustom

Always implement exponential backoff when handling 429 responses.