API Reference
Base URL
https://api.custodianlabs.com/v1
Authentication
All endpoints require your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEY
Endpoints
POST /v1/process
Processes a text input and returns the AI-generated result.
Request
POST /v1/process
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"text": "The text content you want to process"
}
Request body
| Field | Type | Required | Description |
|---|---|---|---|
text | string | Yes | The input text to process. Consumed characters = length of this string. |
Response 200 OK
{
"result": "The AI-processed output",
"characters_used": 312,
"credits_remaining": 49688
}
| Field | Type | Description |
|---|---|---|
result | string | Processed output |
characters_used | number | Characters consumed by this request |
credits_remaining | number | Remaining credits on your API key |
Error responses
| Status | Body error | Description |
|---|---|---|
401 | unauthorized | Missing or invalid API key |
403 | expired | API key has expired |
403 | character_limit_reached | No credits remaining |
403 | not_enough_credits | Request needs more credits than available |
422 | invalid_input | text field is missing or empty |
429 | rate_limited | Too many requests |
500 | server_error | Internal server error |
GET /v1/status
Returns the current status of your API key including credit balance.
Request
GET /v1/status
Authorization: Bearer YOUR_API_KEY
Response 200 OK
{
"valid": true,
"plan_name": "starter",
"character_limit": 10000,
"characters_used": 4200,
"credits_remaining": 5800
}
| Field | Type | Description |
|---|---|---|
valid | boolean | Whether the key is active |
plan_name | string | Your subscription plan |
character_limit | number | Total credits on your plan |
characters_used | number | Credits consumed so far |
credits_remaining | number | Credits still available |
Error response format
All errors return a consistent JSON structure:
{
"error": "error_code",
"message": "A human-readable description of what went wrong."
}
Rate limits
Rate limits are applied per API key. If you exceed the limit you will receive a 429 Too Many Requests response. Wait a moment and retry your request.
Code examples
JavaScript / TypeScript
const res = await fetch('https://api.custodianlabs.com/v1/process', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.CUSTODIAN_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'Hello world' }),
});
if (!res.ok) {
const err = await res.json();
throw new Error(err.message);
}
const { result, credits_remaining } = await res.json();
Python
import requests, os
res = requests.post(
'https://api.custodianlabs.com/v1/process',
headers={'Authorization': f'Bearer {os.environ["CUSTODIAN_API_KEY"]}'},
json={'text': 'Hello world'}
)
res.raise_for_status()
data = res.json()
print(data['result'])
cURL
curl -X POST https://api.custodianlabs.com/v1/process \
-H "Authorization: Bearer $CUSTODIAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Hello world"}'
POST /api/stripe/checkout
Creates a Stripe Checkout Session for a subscription plan.
Request body
{
"planName": "starter"
}
| Field | Type | Required | Description |
|---|---|---|---|
planName | string | Yes | Plan identifier. Must match a key in the price ID map. |
Response
200 OK
{
"url": "https://checkout.stripe.com/pay/cs_test_..."
}
400 Bad Request — unknown plan name
500 Internal Server Error — Stripe error or missing environment variable
Notes
- Requires an authenticated Supabase session.
userIdis extracted server-side from the session cookie. - The
success_urlis/api/stripe/confirm?session_id={CHECKOUT_SESSION_ID}.
GET /api/stripe/confirm
Called by Stripe after a successful payment to verify the session and redirect the user.
Query parameters
| Parameter | Type | Description |
|---|---|---|
session_id | string | Stripe Checkout Session ID |
Response
Redirects to /dashboard/subscriptions?success=true on valid payment, or /dashboard/subscriptions?error=true on failure.
POST /api/stripe/webhook
Stripe webhook handler. Receives and processes checkout.session.completed events.
Headers
| Header | Description |
|---|---|
stripe-signature | Stripe HMAC signature for verification |
Request body
Raw Stripe event payload (JSON).
Response
200 OK — event processed
400 Bad Request — signature verification failed or missing data
500 Internal Server Error — database error
This route requires the raw request body. Do not use a JSON body parser middleware before this route.
POST /api/test-api-key
Proxies a test request to the configured API endpoint using the provided API key.
Request body
{
"apiKey": "cai_your_key_here",
"endpoint": "https://api.example.com/v1/test"
}
| Field | Type | Required | Description |
|---|---|---|---|
apiKey | string | Yes | API key to test |
endpoint | string | No | Target URL. Defaults to API_TEST_ENDPOINT env var. |
Response
200 OK
{
"status": 200,
"ok": true,
"body": { ... }
}
400 Bad Request — missing apiKey
500 Internal Server Error — proxy request failed or no endpoint configured
GET /api/test-csv
Returns a sample CSV file for testing download functionality.
Response
text/csv content with sample data.
Database RPC functions
These PostgreSQL functions are callable via the Supabase client.
validate_api_key(api_key_input, required_chars?)
Validates an API key and checks available word and call quota.
const { data } = await supabase.rpc('validate_api_key', {
api_key_input: 'cai_your_key',
required_chars: 1000,
});
Returns:
{
"valid": true,
"api_key_id": "uuid",
"user_id": "uuid",
"plan_name": "starter",
"character_limit": 10000,
"characters_used": 4200,
"call_limit": 8000,
"calls_used": 120
}
On failure: { "valid": false, "reason": "not_found" | "expired" | "character_limit_reached" | "not_enough_credits" | "call_limit_reached" }
consume_api_key_characters(api_key_input, chars_used)
Atomically increments characters_used by the given amount and calls_used by 1.
const { data } = await supabase.rpc('consume_api_key_characters', {
api_key_input: 'cai_your_key',
chars_used: 500,
});
Returns: { "updated": true, "api_key_id": "uuid", "characters_used": 4700, "calls_used": 121 }
On failure: { "updated": false, "reason": "not_updated" | "invalid_amount" }
decrement_subscription_credits(p_user_id, p_amount)
Decrements character_credits_remaining on the user's active subscription (minimum 0).
await supabase.rpc('decrement_subscription_credits', {
p_user_id: 'user-uuid',
p_amount: 500,
});
decrement_subscription_calls(p_user_id, p_amount)
Decrements call_credits_remaining on the user's active subscription (minimum 0).
await supabase.rpc('decrement_subscription_calls', {
p_user_id: 'user-uuid',
p_amount: 1,
});