API Authentication

5 min read

All WittyForm API requests require authentication via an API key. This guide covers how to generate keys, choose the right scopes, and follow security best practices.

Generating API keys

  1. Log in to your WittyForm dashboard.
  2. Navigate to API Keys in the sidebar.
  3. Click Generate new key.
  4. Enter a descriptive label (e.g., "Production backend" or "Zapier integration").
  5. Select one or more scopes (see below).
  6. Click Create.
The full key is displayed only once. Copy it immediately and store it in a secure location such as an environment variable or a secrets manager.

Key scopes

Each API key can be scoped to limit what it can do. Choose the minimum scope needed for your use case:

ScopePermissionsUse case
readList and retrieve forms, responses, and settingsDashboards, analytics, Zapier triggers
writeCreate and update forms, submit responsesProgrammatic form creation, data import
adminFull access including delete, webhooks, and account settingsBackend services that need complete control

A key with write scope also inherits read permissions. A key with admin scope inherits both read and write.

Granular per-resource scopes

The three scopes above (read, write, admin) are coarse: each one acts as a wildcard across every resource. They are kept for backward compatibility, and you can keep using them. For least privilege, a key can instead hold one or more granular resource:action scopes, where:

  • resource is one of forms, responses, webhooks, quizzes.
  • action is one of read, write, admin (still hierarchical: admin > write > read).

A granular scope only grants access to its own resource. For example, a key with only responses:read can read responses but cannot touch forms, webhooks, or quizzes. The coarse scopes behave as wildcards in the other direction: a legacy read key satisfies any *:read requirement (such as forms:read or quizzes:read), and a legacy admin key satisfies everything.

Endpoint groupReadWriteDelete
Formsforms:readforms:writeforms:admin
Responsesresponses:readresponses:writeresponses:admin
Quizzesquizzes:readquizzes:writequizzes:admin
Webhookswebhooks:readwebhooks:adminwebhooks:admin

Scopes (coarse or granular) are chosen when you create the key, and they cannot be widened afterward. To change a key's access, generate a new key with the scopes you need and revoke the old one.

Bearer token format

Include your API key in the Authorization header using the Bearer scheme, or in the X-API-Key header. Either works:

Authorization: Bearer wf_live_abc123def456ghi789

# or, equivalently:
X-API-Key: wf_live_abc123def456ghi789

API keys are prefixed with wf_live_. Do not pass the key in the URL as a query parameter (such as ?api_key= or ?key=): such requests are rejected with a 400 response and the error code KEY_INVALID.

Example request with curl

curl -X GET "https://wittyform.com/api/v1/forms" \
  -H "Authorization: Bearer wf_live_abc123def456ghi789" \
  -H "Content-Type: application/json"

A successful response returns 200 OK with { "success": true, "data": { ... } }. Every error uses the same envelope, with a stable code and a human-readable message. An invalid key returns:

{
  "success": false,
  "error": {
    "code": "KEY_INVALID",
    "message": "Invalid or expired API key"
  }
}

Possible error codes:

CodeHTTP statusMeaning
UNAUTHENTICATED401No API key was provided
KEY_INVALID401 (400 if passed in the URL)The key is wrong or revoked, or was supplied as a query parameter
KEY_EXPIRED401The key has expired
INSUFFICIENT_SCOPE403The key lacks the scope this endpoint requires
PLAN_REQUIRED403API access requires a Pro or higher plan
RESPONSE_LIMIT_REACHED403The form has reached its response limit
RATE_LIMITED429Too many requests; see the Retry-After header
NOT_FOUND404The requested resource does not exist
FORM_NOT_PUBLISHED422The form is not published, so it cannot accept responses
FORM_CLOSED422The form is closed and no longer accepts responses
VALIDATION_ERROR422The request body failed validation
UNSUPPORTED_MEDIA_TYPE415The request was not sent as application/json
INTERNAL_ERROR500An unexpected server error occurred

Example with JavaScript fetch

const API_KEY = process.env.WITTYFORM_API_KEY;

const response = await fetch('https://wittyform.com/api/v1/forms', {
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.code}: ${error.message}`);
}

const { data } = await response.json();
console.log(`Found ${data.forms.length} forms`);

Example with Python requests

import os
import requests

API_KEY = os.environ["WITTYFORM_API_KEY"]

response = requests.get(
    "https://wittyform.com/api/v1/forms",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
)
response.raise_for_status()

data = response.json()["data"]
print(f"Found {len(data['forms'])} forms")

Idempotent requests

Any create (POST) request can carry an optional Idempotency-Key header. Send a unique value (a UUID works well) and, if you have to retry the request because the response did not reach you, reuse the same key. The API replays the original response with an Idempotent-Replayed: true header instead of creating a duplicate. Reusing a key with a different request body returns 422. Keys are remembered for 24 hours.

curl -X POST "https://wittyform.com/api/v1/forms" \
  -H "Authorization: Bearer wf_live_abc123def456ghi789" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7b9c1d2e-3f40-4a51-8b62-9c0d1e2f3a4b" \
  -d '{ "title": "Contact Form" }'

See the create endpoints in the Forms API and Responses API for full examples.

Key rotation

To rotate a key without downtime:

  1. Generate a new key with the same scopes.
  2. Update your application to use the new key.
  3. Verify that requests succeed with the new key.
  4. Revoke the old key (see below).

Both keys are valid simultaneously until the old one is revoked, so there is no interruption during the transition.

Revoking keys

  1. Go to API Keys in the sidebar.
  2. Find the key you want to revoke.
  3. Click the Revoke button and confirm.

Revocation is immediate. Any request using the revoked key will receive a 401 Unauthorized response.

Security best practices

  • Never expose keys in client-side code. API keys should only be used in server-side environments. If you need to submit forms from a browser, use the public form URL instead.
  • Use the minimum scope. A read-only dashboard only needs the read scope.
  • Rotate keys regularly. Set a calendar reminder to rotate production keys every 90 days.
  • Use environment variables. Store keys in .env files (excluded from version control) or a secrets manager like AWS Secrets Manager, HashiCorp Vault, or Doppler.
  • Monitor usage. Each key shows a Last used timestamp on the API Keys page. If you suspect a key is compromised, revoke it immediately.
  • Use separate keys per environment. Create distinct keys for development, staging, and production.
$37 · paid once

Ready to Build Forms That Actually Convert?

Start free and keep the five forms for good, or pay once and unlock all of it — unlimited forms, ten team members, AI, CRM, white label and the API. No renewal, ever.

14-day money-backInstant accessNo subscription, ever
API Authentication