const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({name: 'Production Key', expires_at: '2026-07-15T10:30:00Z'})
};
fetch('https://api.factify.com/v1/api-keys', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.factify.com/v1/api-keys"
payload = {
"name": "Production Key",
"expires_at": "2026-07-15T10:30:00Z"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.factify.com/v1/api-keys"
payload := strings.NewReader("{\n \"name\": \"Production Key\",\n \"expires_at\": \"2026-07-15T10:30:00Z\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.factify.com/v1/api-keys")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Production Key\",\n \"expires_at\": \"2026-07-15T10:30:00Z\"\n}")
.asString();curl --request POST \
--url https://api.factify.com/v1/api-keys \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Production Key",
"expires_at": "2026-07-15T10:30:00Z"
}
'{
"id": "key_01h2xcejqtf2nbrexx3vqjhp41",
"org_id": "org_01h2xcejqtf2nbrexx3vqjhp41",
"name": "Production Key",
"prefix": "ffy_prod_01kdx2z2...",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:35:00Z",
"is_active": true,
"key": "ffy_prod_EXAMPLE_KEY_DO_NOT_USE",
"expires_at": "2026-01-15T10:30:00Z",
"revoked_at": "2023-11-07T05:31:56Z",
"revoked_by": "usr_01h2xcejqtf2nbrexx3vqjhp41",
"revoked_comment": "rotated"
}{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Request body is malformed or missing required fields."
}{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Missing or invalid authentication credentials."
}{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "You do not have permission to perform this action on this resource."
}{
"type": "about:blank",
"title": "Unprocessable Entity",
"status": 422,
"detail": "Request body failed validation.",
"errors": [
{
"location": "body.email",
"message": "expected string to match format 'email'",
"value": "not-an-email"
}
]
}{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Rate limit exceeded. Retry after the period indicated by the Retry-After header."
}{
"type": "about:blank",
"title": "Internal Server Error",
"status": 500,
"detail": "An unexpected error occurred. Retry, and contact support if the issue persists."
}Create an API key
Creates a new API key for the caller’s organization. The full secret is returned exactly once in the response and cannot be retrieved later — store it securely on receipt.
Keys cannot be updated; rotate by creating a new key and revoking the old one.
Authorization: requires organization admin role.
const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({name: 'Production Key', expires_at: '2026-07-15T10:30:00Z'})
};
fetch('https://api.factify.com/v1/api-keys', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.factify.com/v1/api-keys"
payload = {
"name": "Production Key",
"expires_at": "2026-07-15T10:30:00Z"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.factify.com/v1/api-keys"
payload := strings.NewReader("{\n \"name\": \"Production Key\",\n \"expires_at\": \"2026-07-15T10:30:00Z\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.factify.com/v1/api-keys")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Production Key\",\n \"expires_at\": \"2026-07-15T10:30:00Z\"\n}")
.asString();curl --request POST \
--url https://api.factify.com/v1/api-keys \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Production Key",
"expires_at": "2026-07-15T10:30:00Z"
}
'{
"id": "key_01h2xcejqtf2nbrexx3vqjhp41",
"org_id": "org_01h2xcejqtf2nbrexx3vqjhp41",
"name": "Production Key",
"prefix": "ffy_prod_01kdx2z2...",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:35:00Z",
"is_active": true,
"key": "ffy_prod_EXAMPLE_KEY_DO_NOT_USE",
"expires_at": "2026-01-15T10:30:00Z",
"revoked_at": "2023-11-07T05:31:56Z",
"revoked_by": "usr_01h2xcejqtf2nbrexx3vqjhp41",
"revoked_comment": "rotated"
}{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Request body is malformed or missing required fields."
}{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Missing or invalid authentication credentials."
}{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "You do not have permission to perform this action on this resource."
}{
"type": "about:blank",
"title": "Unprocessable Entity",
"status": 422,
"detail": "Request body failed validation.",
"errors": [
{
"location": "body.email",
"message": "expected string to match format 'email'",
"value": "not-an-email"
}
]
}{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Rate limit exceeded. Retry after the period indicated by the Retry-After header."
}{
"type": "about:blank",
"title": "Internal Server Error",
"status": 500,
"detail": "An unexpected error occurred. Retry, and contact support if the issue persists."
}Authorizations
Bearer authentication using a factapi-issued API key
(ffy_<env>_<base32_uuid><base62_random>). Cookie-based
sessions are accepted automatically by user-facing endpoints
but are not surfaced as an OpenAPI auth scheme.
Headers
Optional. Factify-staff acting-as override: when set, factapi
resolves the request against this organization instead of the
session-bound one. Honored only for callers with @factify.com
emails; non-staff requests carrying this header are rejected
with 403. The value is a typed org id (e.g.
org_01h2xcejqtf2nbrexx3vqjhp41).
^org_[0-9a-hjkmnp-tv-z]{26}$"org_01h2xcejqtf2nbrexx3vqjhp41"
Body
Fields required to create a new API key.
Human-readable name for the key (e.g. "Production", "Staging").
"Production Key"
Optional expiration timestamp. If not set, the key does not expire. When set, must be in the future and within one year of now.
Security note: keys without expiration should be rotated periodically.
"2026-07-15T10:30:00Z"
Response
The newly created API key, including the full secret (returned exactly once).
The newly created API key, including the full secret. The
secret (key) is only returned in this response and cannot be
retrieved later — store it securely now.
Unique TypedID for the API key.
Pattern: key_[0-9a-hjkmnp-tv-z]{26}
"key_01h2xcejqtf2nbrexx3vqjhp41"
TypedID of the organization this key belongs to.
Pattern: org_[0-9a-hjkmnp-tv-z]{26}
"org_01h2xcejqtf2nbrexx3vqjhp41"
Human-readable name for the key.
"Production Key"
Truncated key prefix for identification in UI. Safe to log.
"ffy_prod_01kdx2z2..."
Timestamp when the key was created.
"2025-01-15T10:30:00Z"
Timestamp of the last metadata update.
"2025-01-15T10:35:00Z"
Whether the key is currently usable for authentication.
true
The full API key token, format
ffy_{env}_{base32_uuid}{base62_random} (per ADR-008).
Only returned in this response — store it securely.
SECURITY: never log this value; redact it from any persisted request or response payloads.
"ffy_prod_EXAMPLE_KEY_DO_NOT_USE"
Expiration timestamp. Absent if the key does not expire.
"2026-01-15T10:30:00Z"
Timestamp when the key was revoked. Absent on creation.
TypedID of the user who revoked the key. Absent on creation.
Pattern: usr_[0-9a-hjkmnp-tv-z]{26}
"usr_01h2xcejqtf2nbrexx3vqjhp41"
Optional revocation reason. Absent on creation.
"rotated"