Create an API key
Create an API key.
Registers a developer and issues a key. It is the one unauthenticated write endpoint — you need a key to get a key — so it is metered per IP address instead of per key.
The plaintext key is returned exactly once. Only a SHA-256 hash and a short display prefix are stored, so it cannot be retrieved afterwards; losing it means issuing another.
The route does not send a plan, so every developer registered here is created on the free plan. Registration is idempotent on email: an existing developer receives an additional key rather than a duplicate account.
Because this endpoint is not metered by key, it has no plan_limit row and no X-RateLimit-* headers. Its limit is the per-IP throttle described on the Rate limits page.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
name | body | string | Required | Required. Up to 200 characters. |
email | body | string (email) | Required | Required. Up to 200 characters, lower-cased before storage, and checked against a permissive address shape. |
organization | body | string | Optional | Optional. Truncated to 200 characters. |
useCase | body | string | Optional | Optional. Truncated to 500 characters. |
keyName | body | string | Optional | Not in the OpenAPI document. The route reads it and stores it as the key's display name; it defaults to default. |
Example request
The curl works against the live API once OZITUMA_API_KEY holds your key.
curl -X POST "https://ozituma.com/api/v1/developers" \
-H "Content-Type: application/json" \
-d '{"name":"Ada","email":"[email protected]","useCase":"language learning app"}'The same request, raw
POST /api/v1/developers HTTP/1.1
Host: ozituma.com
Content-Type: application/json
{"name":"Ada","email":"[email protected]","useCase":"language learning app"}Example response
{
"developer": {
"id": "6f9c1e2a-…",
"name": "Ada",
"email": "[email protected]",
"plan": "free"
},
"apiKey": "ozt_live_…",
"keyPrefix": "ozt_live_…",
"warning": "Store this key now. It is hashed on our side and cannot be shown again.",
"usage": "Send it as the X-API-Key header. See /docs for endpoints."
}The 201 body the route returns, with the account id and the key redacted. warning and usage are the route's own strings. The document types this response as a plain object without listing the fields.
Errors
Responses the OpenAPI document lists
| Status | What the document says |
|---|---|
201 | Key created. The plaintext key is returned once and never again. |
400 | Invalid body |
429 | Daily quota exceeded for your plan |
Codes this route can return
| Status | Code | When |
|---|---|---|
| 400 | invalid_parameter | A query or body parameter is missing, malformed, or outside its range. The message names the parameter. |
| 429 | rate_limited | POST /api/v1/developers only: more than 5 key requests from one IP address in a 15-minute window. The response carries Retry-After: 900. |
| 500 | internal_error | An unexpected server error. The message is generic on purpose; a stack trace or a SQL error is never returned to a caller. |
The body of every failure is the same. Errors explained →
Where the document and the implementation differ
Read from the route source and the OpenAPI document together. Where the two disagree, this is what each one says.
- The document declares this operation's 429 as the shared QuotaExceeded response ("Daily quota exceeded for your plan"). This route uses no key quota: its 429 is
rate_limited, from the per-IP throttle, and carriesRetry-After: 900. - The route can also return 500 (
internal_error), which the document does not list. - The route accepts a
keyNamebody field that the document does not list. - The document marks the request body required and
nameandemailrequired. The route enforces exactly that: either field missing or malformed is a 400.