Skip to main content

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

FieldTypeRequiredDescription
textstringYesThe input text to process. Consumed characters = length of this string.

Response 200 OK

{
"result": "The AI-processed output",
"characters_used": 312,
"credits_remaining": 49688
}
FieldTypeDescription
resultstringProcessed output
characters_usednumberCharacters consumed by this request
credits_remainingnumberRemaining credits on your API key

Error responses

StatusBody errorDescription
401unauthorizedMissing or invalid API key
403expiredAPI key has expired
403character_limit_reachedNo credits remaining
403not_enough_creditsRequest needs more credits than available
422invalid_inputtext field is missing or empty
429rate_limitedToo many requests
500server_errorInternal 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
}
FieldTypeDescription
validbooleanWhether the key is active
plan_namestringYour subscription plan
character_limitnumberTotal credits on your plan
characters_usednumberCredits consumed so far
credits_remainingnumberCredits 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"
}
FieldTypeRequiredDescription
planNamestringYesPlan 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. userId is extracted server-side from the session cookie.
  • The success_url is /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

ParameterTypeDescription
session_idstringStripe 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

HeaderDescription
stripe-signatureStripe 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

note

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"
}
FieldTypeRequiredDescription
apiKeystringYesAPI key to test
endpointstringNoTarget 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,
});