Die NueForm-API verwendet die üblichen HTTP-Statuscodes und gibt strukturierte JSON-Fehlerantworten zurück. Diese Anleitung behandelt das Antwortformat, alle möglichen Fehlercodes und Muster für die Fehlerbehandlung in deinem Code.
Antwortformate
Erfolgsantworten
Erfolgreiche API-Aufrufe geben ein JSON-Objekt mit einem data-Feld zurück. Listen-Endpunkte enthalten zusätzlich ein meta-Feld mit Paginierungsinformationen.
{
"data": {
"id": "clx1abc2d3e4f5g6h7i8j9k0",
"title": "Customer Feedback",
"status": "published",
"created_at": "2026-01-15T09:30:00.000Z"
}
}
Mit Paginierungs-Metadaten:
{
"data": [
{
"id": "clx1abc2d3e4f5g6h7i8j9k0",
"title": "Customer Feedback"
},
{
"id": "clx2def3g4h5i6j7k8l9m0n1",
"title": "Employee Survey"
}
],
"meta": {
"page": 1,
"per_page": 20,
"total": 150,
"total_pages": 8
}
}
Fehlerantworten
Fehlerantworten geben ein JSON-Objekt mit einem error-Feld zurück, das den Fehler-code, eine menschenlesbare message und den HTTP-status-Code enthält.
{
"error": {
"code": "NOT_FOUND",
"message": "The requested form could not be found.",
"status": 404
}
}
Fehlercodes
Die folgende Tabelle listet alle Fehlercodes auf, die die API zurückgibt:
| Code | HTTP-Status | Beschreibung |
|---|---|---|
BAD_REQUEST | 400 | Die Anfrage ist fehlerhaft, es fehlen Pflichtfelder oder sie enthält ungültige Werte. |
UNAUTHORIZED | 401 | Die Authentifizierung ist fehlgeschlagen. Der API-Schlüssel fehlt, ist fehlerhaft, widerrufen oder abgelaufen. |
FORBIDDEN | 403 | Der authentifizierte Benutzer hat keine Berechtigung für diese Aktion. |
NOT_FOUND | 404 | Die angeforderte Ressource existiert nicht oder ist für den authentifizierten Benutzer nicht zugänglich. |
CONFLICT | 409 | Die Anfrage steht im Konflikt mit dem aktuellen Zustand der Ressource (z. B. doppelter Eintrag oder ein Formular, das noch nicht veröffentlicht werden kann). |
RATE_LIMITED | 429 | Zu viele Anfragen. Details findest du unter Rate Limits. |
INTERNAL_ERROR | 500 | Auf dem Server ist ein unerwarteter Fehler aufgetreten. |
Beispiele für Fehlerantworten
400 Bad Request
Wird zurückgegeben, wenn der Anfrage-Body ungültig ist oder Pflichtfelder fehlen.
{
"error": {
"code": "BAD_REQUEST",
"message": "Field 'title' is required.",
"status": 400
}
}
401 Unauthorized
Wird zurückgegeben, wenn die Authentifizierung aus irgendeinem Grund fehlschlägt.
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or expired API key.",
"status": 401
}
}
403 Forbidden
Wird zurückgegeben, wenn der Benutzer zwar authentifiziert ist, aber keine Berechtigung hat. Häufige Ursachen sind der Zugriff auf Ressourcen eines anderen Benutzers oder die Nutzung einer Funktion, die im aktuellen Tarif nicht enthalten ist.
{
"error": {
"code": "FORBIDDEN",
"message": "API access is not available on your current plan.",
"status": 403
}
}
404 Not Found
Wird zurückgegeben, wenn die angeforderte Ressource nicht existiert.
{
"error": {
"code": "NOT_FOUND",
"message": "The requested form could not be found.",
"status": 404
}
}
409 Conflict
Wird zurückgegeben, wenn die Anfrage ein Duplikat erzeugen würde oder mit vorhandenen Daten in Konflikt steht.
{
"error": {
"code": "CONFLICT",
"message": "A form with this slug already exists.",
"status": 409
}
}
409 Conflict — Veröffentlichung blockiert
Wird von POST /api/v1/forms/:id/publish (und von PUT /api/v1/forms/:id mit published: true) zurückgegeben, wenn das Formular noch nicht live gehen kann. Es wird nichts veröffentlicht. reason nennt den Grund, und question_ids listet die zu korrigierenden Fragen auf. Ist upgrade_required true, braucht der Plan des Formularinhabers ein Upgrade.
{
"error": {
"code": "CONFLICT",
"message": "E-signature contracts need a Pro plan to go live. Upgrade to publish this form, or remove the contract question.",
"status": 409,
"reason": "contract_requires_pro",
"question_ids": ["66a1b2c3d4e5f6a7b8c9d001"],
"upgrade_required": true
}
}
reason | Was zu beheben ist |
|---|---|
contract_requires_pro, data_node_requires_pro, booking_requires_pro, payment_connect_requires_pro, webhook_requires_pro | Der Plan des Formularinhabers (bei Team-Formularen der des Team-Owners) enthält diese Funktion nicht. Mach ein Upgrade oder entferne sie: Lösche die Frage, leere webhook_url oder stelle die Zahlungsfrage auf die Standardoption um. |
booking_* | Eine Buchungsfrage kann noch keine Buchungen annehmen: Verbinde ihren Kalender (erneut) oder ergänze ihre Zeiten, ihre Zeitzone, ihre Daueroptionen oder die Nummer für Bestätigungsanrufe. |
email_node_* | Einem E-Mail-Schritt fehlen Empfänger, Absendername, Betreff oder Inhalt, oder er sendet an {respondent.email} in einem Formular, das keine Anmeldung verlangt. |
fuse_* | Eine Fuse-Frage hat kein Fuse-Formular, ihr Fuse-Formular fehlt oder ist nicht veröffentlicht, oder ihre angeheftete Session wurde archiviert. |
composite_* | Ein Composite, das das Formular verwendet, ist nicht veröffentlicht, gelöscht oder leer; composite_ids listet sie auf. |
Erstellst du ein Formular mit published: true und wird die Veröffentlichung blockiert, wird das Formular trotzdem als Entwurf angelegt: Die Antwort ist 201 Created mit einem publish_error-Objekt (reason, message, question_ids, upgrade_required), sodass ein erneuter Versuch nie ein Duplikat erzeugt. Gibt PUT /api/v1/forms/:id diesen Fehler zurück, wurden deine Änderungen gespeichert (saved_as_draft: true), und nur die Veröffentlichung ist fehlgeschlagen.
429 Rate Limited
Wird zurückgegeben, wenn das Rate Limit überschritten wurde. Enthält einen Retry-After-Header.
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Please try again later.",
"status": 429
}
}
500 Internal Error
Wird zurückgegeben, wenn ein unerwarteter Serverfehler auftritt. Wenn das Problem bestehen bleibt, wende dich an den Support.
{
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal error occurred.",
"status": 500
}
}
Fehler der Voice-API
Die Voice-API ergänzt diese Codes. Nichts, was mit einem Fehler endet, wird berechnet, und die zusätzlichen Felder stehen neben code innerhalb von error.
| Code | HTTP-Status | Beschreibung |
|---|---|---|
INSUFFICIENT_CREDITS | 402 | Dein Guthaben reicht für die Anfrage nicht. credits_required, credits_available und top_up_url sagen, wie viel fehlt und wo du mehr bekommst. |
TEXT_TOO_LONG | 413 | Mehr gesprochene Zeichen, als eine Anfrage annimmt (max_characters). Teile den Text auf mehrere Anfragen auf. |
CONTENT_BLOCKED | 422 | Der Text darf nach unseren Nutzungsrichtlinien nicht gesprochen werden (categories). |
VOICE_UNAVAILABLE | 422 | Die Stimme existiert nicht oder du darfst sie nicht verwenden (voice). |
LANGUAGE_NOT_SUPPORTED_BY_VOICE | 422 | Die Stimme kann diese Sprache nicht sprechen (voice, language, supported_languages). |
LINE_LANGUAGE_NOT_SUPPORTED_BY_VOICE | 422 | Einige Zeilen sind in einer Sprache, die ihre eigene Stimme nicht sprechen kann. mismatches listet jede davon auf (speaker, voice, voice_name, language, line_count), dazu bis zu vier suggestions mit Stimmen, die diese Sprache sprechen. Ist native_voices false, spricht keine Stimme genau diese Sprache: Die Vorschläge sprechen eine verwandte Sprache und klingen womöglich nicht richtig. |
INVALID_SCRIPT | 422 | Das Skript kann so nicht gesprochen werden (reason, zum Beispiel speaker_not_cast). |
CONCURRENCY_LIMIT | 429 | Zu viele gleichzeitig laufende Generierungen für dein Konto. Versuche es nach Retry-After erneut. |
CAPACITY | 503 | Gerade keine Kapazität. Versuche es nach Retry-After erneut. |
WARMING_UP | 503 | Die Sprach-Engine startet gerade. Versuche es nach Retry-After erneut. |
MODERATION_UNAVAILABLE | 503 | Der Text konnte gerade nicht geprüft werden. Versuche es nach Retry-After erneut. |
AI_UNAVAILABLE | 503 | auto_convert oder auto_cast konnte gerade nicht laufen. Versuche es nach Retry-After erneut. |
DELETE_INCOMPLETE | 503 | Ein Löschvorgang wurde unterbrochen. Sende ihn erneut, um ihn abzuschließen. |
FORBIDDEN (403) und CONFLICT (409) nennen den Grund in reason: zum Beispiel key_limit_reached, account_gate oder scope_not_allowed bzw. quote_exceeds_max_credits, idempotency_conflict oder request_in_progress.
{
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "This request needs 30 credits; 12 are available. Nothing was charged.",
"status": 402,
"credits_required": 30,
"credits_available": 12,
"top_up_url": "https://nueform.io/wallet?tab=buy"
}
}
Fehler im Code behandeln
JavaScript
Baue einen wiederverwendbaren API-Client, der auf Fehler prüft und typisierte Exceptions wirft:
class NueFormApiError extends Error {
constructor(code, message, status) {
super(message);
this.name = "NueFormApiError";
this.code = code;
this.status = status;
}
}
async function nueformRequest(path, options = {}) {
const response = await fetch(`https://app.nueform.io/api/v1${path}`, {
...options,
headers: {
"Authorization": `Bearer ${process.env.NUEFORM_API_KEY}`,
"Content-Type": "application/json",
...options.headers,
},
});
const body = await response.json();
if (!response.ok) {
const { code, message, status } = body.error;
throw new NueFormApiError(code, message, status);
}
return body;
}
// Usage
async function getForm(formId) {
try {
const { data } = await nueformRequest(`/forms/${formId}`);
return data;
} catch (error) {
if (error instanceof NueFormApiError) {
switch (error.code) {
case "NOT_FOUND":
console.error(`Form ${formId} does not exist.`);
return null;
case "UNAUTHORIZED":
console.error("Check your API key configuration.");
break;
case "RATE_LIMITED":
console.warn("Rate limited. Implement backoff and retry.");
break;
default:
console.error(`API error [${error.code}]: ${error.message}`);
}
}
throw error;
}
}
Python
import os
import requests
class NueFormApiError(Exception):
def __init__(self, code, message, status):
super().__init__(message)
self.code = code
self.status = status
class NueFormClient:
def __init__(self, api_key=None):
self.api_key = api_key or os.environ["NUEFORM_API_KEY"]
self.base_url = "https://app.nueform.io/api/v1"
def request(self, method, path, **kwargs):
url = f"{self.base_url}{path}"
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
}
response = requests.request(method, url, headers=headers, **kwargs)
body = response.json()
if not response.ok:
error = body["error"]
raise NueFormApiError(
error["code"], error["message"], error["status"]
)
return body
# Usage
client = NueFormClient()
try:
result = client.request("GET", f"/forms/{form_id}")
form = result["data"]
print(f"Form: {form['title']}")
except NueFormApiError as e:
if e.code == "NOT_FOUND":
print(f"Form {form_id} does not exist.")
elif e.code == "UNAUTHORIZED":
print("Check your API key configuration.")
elif e.code == "RATE_LIMITED":
print("Rate limited. Implement backoff and retry.")
else:
print(f"API error [{e.code}]: {e}")
Best Practices
Prüfe immer das error-Feld
Gehe niemals allein aufgrund eines vorhandenen Bodys davon aus, dass eine Antwort erfolgreich war. Prüfe immer auf das error-Feld oder den HTTP-Statuscode, bevor du die Antwort weiterverarbeitest.
Nutze konkrete Fehlercodes für die Ablaufsteuerung
Werte error.code aus (z. B. NOT_FOUND, RATE_LIMITED) statt des HTTP-Statuscodes. Fehlercodes sind stabil, aussagekräftig und in der Anwendungslogik leichter nachzuvollziehen.
Protokolliere Fehlerdetails
Nimm die vollständige Fehlerantwort zum Debuggen in deine Logs auf. code, message und status liefern zusammen genug Kontext, um die meisten Probleme zu diagnostizieren, ohne die Anfrage reproduzieren zu müssen.
Behandle 5xx-Fehler mit Retries
Serverfehler (500 Internal Error) sind in der Regel vorübergehend. Implementiere für diese Antworten eine Retry-Logik mit exponentiellem Backoff. Begrenze die Wiederholungen auf 2--3 Versuche, bevor du abbrichst.
Zeige Fehlermeldungen nicht den Endnutzern
API-Fehlermeldungen richten sich an Entwickler, nicht an Endnutzer. Übersetze API-Fehler in deiner Anwendung in nutzerfreundliche Meldungen.
Wenn du dauerhaft 500 Internal Error-Antworten erhältst, wende dich bitte mit den Anfragedetails und Zeitstempeln an den Support. Serverfehler werden bei uns protokolliert und untersucht.