Winnerr’s API is currently used by the Winnerr web application, AI agent runtime, voice/SMS webhooks, email integrations, and MCP clients. This page documents the API behavior that exists in the repository today so developers do not build against stale /v1 examples or unsupported SDK assumptions.
The general third-party REST API and public SDKs are not documented as launched surfaces in this repository. Treat app APIs as product-internal unless a route, token flow, and support contract are explicitly documented for your integration.
Base URLs
Local development uses two separate Next.js applications:
| Surface | Local URL | Purpose |
|---|
| Web app | http://localhost:3000 | Authenticated CRM UI |
| API app | http://localhost:3002 | API routes, webhooks, workflow endpoints, MCP endpoint |
The API app routes are mounted from apps/api/app. Most application routes live under /api/..., while provider webhooks live under /webhooks/....
# Local API health check
curl http://localhost:3002/health
Authentication model
There are two supported authentication patterns in the codebase:
Web app to API app
When apps/app calls apps/api, the web app must forward the user’s session token as a bearer token:
const token = await session.getToken();
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/people`, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
API routes in apps/api should read this token with getAuthFromRequest(request), then scope database work to the returned userId and orgId.
import { getAuthFromRequest } from '@/lib/auth-helper';
export async function GET(request: Request) {
const { userId, orgId } = await getAuthFromRequest(request);
if (!(userId && orgId)) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
// Query data using organizationId: orgId
}
Do not use auth() inside apps/api routes for frontend-to-API requests. auth() only reads the same-server session context; the separate API app receives bearer tokens from the web app.
MCP OAuth 2.1 with PKCE
The OAuth endpoints in apps/api/app/oauth are for MCP-style clients, not a broad public app marketplace. The authorization endpoint accepts only known MCP client IDs and redirect URI patterns, requires PKCE, stores a short-lived authorization code, and issues a one-hour JWT access token for the MCP resource.
Supported endpoints:
| Method | Path | Notes |
|---|
GET | /oauth/authorize | Requires client_id, redirect_uri, response_type=code, code_challenge, and code_challenge_method. |
POST | /oauth/token | Expects application/x-www-form-urlencoded data with grant_type=authorization_code, code, redirect_uri, client_id, and code_verifier. |
Token responses include MCP metadata such as the MCP server URL, user ID, organization ID, organization name, and granted permissions.
Response shapes
Response bodies are route-specific today. Do not assume every route returns the same envelope, pagination metadata, or RFC 7807 error shape. Check the route implementation or generated OpenAPI artifact for the endpoint you are using.
Common expectations:
- Authenticated application routes should return
401 when userId or orgId is missing.
- Multi-tenant data access must be scoped to the authenticated organization.
- Webhook routes may use provider-specific validation and response formats, such as TwiML for Twilio voice/SMS flows.
- External API calls should validate response shape and log failures with
parseError().
Before documenting or consuming an endpoint
- Confirm the route exists in
apps/api/app.
- Confirm the authentication method the route expects.
- Confirm the request body and response shape from the route code or tests.
- Avoid adding public examples for routes that are product-internal or provider-webhook-only.
- If the same behavior is needed by more than one surface, keep shared logic in a package rather than duplicating it in route files.