Skip to Content

API Keys

Genfeed API keys are long-lived gf_ bearer credentials for server-to-server calls, automation, CI, and MCP clients. They authenticate the same REST API as a Better Auth session token, but they belong to the user and organization that minted them rather than to a browser session.

Keys are shown once at creation or rotation, stored hashed, and can be revoked at any time.

Key Format

EnvironmentPrefixExample
Productiongf_live_gf_live_xxx
Non-productiongf_test_gf_test_xxx

Any bearer token starting with gf_ is routed to API-key authentication; everything else is validated as a Better Auth JWT.

Create a Key in the App

  1. Open organization settings at /{orgSlug}/~/settings/api-keys.
  2. In Genfeed API keys, enter a label and an optional description.
  3. Pick a scope preset — MCP, Read, or Content — or select individual scopes.
  4. Optionally set an expiry date, a per-key rate limit, and an IP allowlist.
  5. Create the key and copy it immediately. The plaintext value is displayed once and cannot be retrieved again.

MCP clients can use the guided Connect flow in your instance’s web app (/connect, for example http://localhost:3000/connect) instead, which creates an MCP-scoped key and runs an authenticated connection check. See MCP Server.

Store the key where your integration runs:

export GENFEED_API_KEY=gf_live_xxx

Treat API keys like passwords. Do not commit them or paste them into shared logs.

Create a Key via the API

Key management endpoints authenticate with a Better Auth session token:

curl -X POST http://localhost:3010/v1/api-keys \ -H "Authorization: Bearer $GENFEED_JWT" \ -H "Content-Type: application/json" \ -d '{ "label": "Publishing automation", "category": "genfeedai", "description": "Nightly draft + schedule job", "scopes": ["videos:read", "videos:create", "posts:draft"], "expiresAt": "2027-01-01T00:00:00Z", "allowedIps": ["203.0.113.10"] }'

Responses are JSON:API documents. Creation is the only time the plaintext key attribute is returned:

{ "data": { "type": "api-key-full", "id": "apikey_123", "attributes": { "key": "gf_live_xxx", "label": "Publishing automation", "scopes": ["videos:read", "videos:create", "posts:draft"], "expiresAt": "2027-01-01T00:00:00.000Z", "isRevoked": false, "usageCount": 0 } } }

Every other endpoint serializes as api-key and omits the key attribute.

FieldRequiredNotes
labelyes1–100 characters.
categoryyesgenfeedai for platform keys.
descriptionnoUp to 500 characters.
scopesnoSubset of the self-service scopes below. Defaults to video, image, and analytics scopes.
expiresAtnoISO 8601 date. Omit for a non-expiring key.
rateLimitnoPer-key requests per minute. Leave unset for no ceiling.
allowedIpsnoExact client IPs allowed to use the key. Empty means no IP restriction.

Each user may hold 10 active keys. Creating an eleventh returns 400 API Key Limit Reached — revoke or rotate an existing key first.

Manage Keys

MethodPathPurpose
POST/api-keysCreate a key. Returns the plaintext value once.
GET/api-keysList active keys with page, limit, and search query parameters.
GET/api-keys/{apiKeyId}Fetch one key’s metadata.
PATCH/api-keys/{apiKeyId}Update label, description, scopes, expiry, rate limit, or IP allowlist.
POST/api-keys/{apiKeyId}/rotateIssue a replacement key and revoke the original.
POST/api-keys/{apiKeyId}/verify-mcpRun a bounded tool-discovery check against the configured MCP service.
DELETE/api-keys/{apiKeyId}Revoke a key immediately.
POST/api-keys/validateCheck whether a key is currently valid.

Rotation has no grace period: the replacement is issued and the original is revoked in the same request, so deploy the new value before rotating. Revocation takes effect on the next request — revoked and expired keys fail authentication.

Use a Key

Both bearer schemes are accepted:

Authorization: Bearer gf_live_xxx
Authorization: ApiKey gf_live_xxx
curl http://localhost:3010/v1/posts \ -H "Authorization: Bearer $GENFEED_API_KEY"

The request authenticates as the key’s user and organization for tenancy. Authorization is the key’s explicit scopes — never the issuer’s org-admin role. Present the key only in the Authorization header; a key in the URL path or query string is rejected.

The same key authenticates your instance’s MCP server at http://localhost:3014/mcp. MCP OAuth tokens issued through a browser consent flow are bound to the MCP resource and are rejected on the REST API.

Key surfaces

SurfaceCredentialPresentationEffective authorization
Public RESTUser gf_ keyAuthorization: Bearer or ApiKeyExplicit key scopes. Tenancy is the issuing user and org. Org-admin membership is not inherited.
MCP /mcpSame gf_ key or MCP OAuth sessionBearer header onlyMCP user unless the key has an explicit admin scope and the issuer is org admin or owner.
/auth/whoamiSession or gf_ keyBearerAPI keys never advertise owner/admin without admin scope. Scopes never default to *.
Telegram /connectUser gf_ keyPrivate-chat command, then cached apiKeyIdfindByKey at connect. Each later command re-reads the row; a revoked or expired key drops the chat identity.
Agent Auth.md registrationUser gf_ key minted after claimBearer after exchangeSame REST/MCP rules. Revoke goes through findByKey then revoke.
Internal files, notifications, Slack, MCP server-to-serverGENFEEDAI_API_KEY envBearerService identity, not a tenant org-admin.
Provider webhooks (HeyGen, Kling, OpusPro, Leonardo)Vendor shared secret?token= and/or x-webhook-tokenNot a gf_ key. Vendors that have no HMAC only hit the registered URL. HMAC webhooks (Stripe) stay header-only.

A revoked gf_ key fails findByKey / findActiveById on every surface above. MCP does not cache successful gf_ whoami lookups.

Per-endpoint @RequiredScopes coverage is separate from this contract. Missing scope metadata on a collection does not restore implicit org-admin.

Scopes

Scopes are checked per endpoint. Requests missing a required scope return 401 Insufficient permissions.

FamilyScopes
Videosvideos:read, videos:create, videos:update, videos:delete
Imagesimages:read, images:create, images:update, images:delete
Promptsprompts:read, prompts:create, prompts:update, prompts:delete
Articlesarticles:read, articles:create
Postsposts:draft, posts:schedule, posts:approve, posts:publish
Workspacebrands:read, credits:read, analytics:read

The four self-service presets are:

PresetIntent
readRead-only access to content, brands, credits, and analytics.
contentRead plus content creation, including drafts. No scheduling or publishing.
mcpContent creation plus scheduling and approval. Approval-first: excludes direct publishing.
fullEvery self-service scope, including posts:publish and destructive updates.

posts:create remains as a backward-compatible alias for draft creation. Privileged scopes (admin, credits:provision, managed-inference:execute) and wildcards are rejected at creation — they exist only for keys minted server-side.

Rate Limits

Each API-key request is counted in a 60-second sliding window per key. A self-hosted instance has no plans, so the per-key rateLimit is honored. Leaving it unset means no ceiling.

Exceeding the ceiling returns 401 Rate limit exceeded, the same status class as other API-key authentication failures. Back off and retry after the window closes.

Key-management endpoints are limited separately and return 429 Too Many Requests with X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After headers: 5 requests per minute for create and rotate, 10 for MCP verification, and 30 for validation.

Errors

StatusCondition
401Missing Authorization header, unsupported scheme, or malformed value.
401Invalid, expired, or revoked key.
401Required scope missing for the endpoint.
401Client IP outside the key’s allowlist.
401Per-key rate-limit ceiling exceeded.
401MCP OAuth token used outside the MCP resource.
400API Key Limit Reached — the user already holds 10 active keys.

Without a login wall

If your instance runs with the Better Auth login wall turned off (see Configuration), requests without a token resolve to the default local identity, so a key is only required when you want explicit, scoped credentials. Once you expose the instance beyond localhost, enable the login wall and use scoped keys; see Reverse Proxy and HTTPS.

Last updated on