Skip to content
Last updated

Errors

The Frontline Public API returns errors as JSON with a consistent envelope, an HTTP status code, and a machine-readable error code.

Error envelope

{
    "ok": false,
    "error": {
        "code": "bad_request",
        "message": "Human-readable description of what went wrong.",
        "details": {
            /* optional, present on validation errors */
        }
    }
}
  • ok is always false on error responses.
  • error.code is a stable, machine-readable string. Branch on this instead of parsing message.
  • error.message is meant for humans and may change without notice.
  • error.details is optional and appears mainly on validation failures (e.g. zod issues from the request body or query).

Status codes and error codes

HTTPerror.codeWhen it happens
400bad_requestValidation failed (body, params, query). Inspect error.details for the offending fields.
400cli_outdatedCaller is @getfrontline/cli below the minimum supported version. See "CLI outdated" below.
401unauthorizedMissing, malformed, revoked, or wrong-scope API key. See Authentication.
402internal_errorSubscription/billing gate (e.g. plan does not allow the operation).
403forbiddenThe key is valid but does not have access to the target resource.
404not_foundThe resource does not exist or has been soft-deleted.
409conflictUniqueness or relational constraint violated (e.g. duplicate name, foreign key).
429internal_errorRate limit exceeded. See Rate limits.
500internal_errorUnexpected server error. Safe to retry once; if it persists, report it.

Validation errors (400 bad_request)

When zod rejects the request body or query string, error.details carries the structured issues:

{
    "ok": false,
    "error": {
        "code": "bad_request",
        "message": "Invalid request",
        "details": {
            "issues": [
                {
                    "path": ["name"],
                    "message": "String must contain at least 1 character(s)",
                    "code": "too_small"
                }
            ]
        }
    }
}

Use details.issues[].path to highlight the bad field in your UI.

The code on each issue is one of:

  • A standard zod code (invalid_type, too_small, too_big, invalid_enum_value, invalid_string, …) for shape errors caught by request validation.
  • One of the Public-API codes below for cross-resource validation:
codeWhen it appears
not_foundA referenced id (aiModelId, customToolIds[], intentIds[], assistantId, tableId, triggeredByAgentIds[], etc.) does not exist or is not accessible from the caller's account.
wrong_kindThe reference exists but does not match the surrounding constraint (e.g. a recordTypeId that does not belong to the given tableId).
wrong_ownerThe reference exists in the platform but belongs to a different account.

Example: node references

When you create a flow or workflow node with references to resources that don't exist, the API rejects the request before persisting anything and returns every offending reference in a single response:

{
    "ok": false,
    "error": {
        "code": "bad_request",
        "message": "One or more node references are invalid",
        "details": {
            "issues": [
                {
                    "path": ["data", "aiModelId"],
                    "code": "not_found",
                    "value": 99999999,
                    "message": "AI model 99999999 does not exist."
                },
                {
                    "path": ["data", "customToolIds", 0],
                    "code": "not_found",
                    "value": 42,
                    "message": "Tool 42 does not exist or is not accessible from this account."
                }
            ]
        }
    }
}

Branch on issue.code === "not_found" to highlight the broken reference; the human-readable message tells the end user which id was rejected.

CLI outdated (400 cli_outdated)

Returned exclusively to the @getfrontline/cli binaries when the client version is older than the minimum the server supports. The payload includes extra context:

{
    "ok": false,
    "error": {
        "code": "cli_outdated",
        "message": "CLI version is too old. Please update @getfrontline/cli.",
        "details": { "minSupportedVersion": "1.0.9" },
        "suggestion": "Update with: npm i -g @getfrontline/cli (or your package manager equivalent)."
    }
}

The CLI handles this automatically and prints suggestion to the user.

async function callApi(path, init) {
    const res = await fetch(`https://prod-api.getfrontline.ai/public/v1${path}`, init);
    const body = await res.json().catch(() => ({}));

    if (!res.ok) {
        const code = body?.error?.code ?? "unknown";
        const message = body?.error?.message ?? res.statusText;
        throw Object.assign(new Error(message), {
            code,
            status: res.status,
            details: body?.error?.details,
        });
    }

    return body;
}

Branch on err.code (unauthorized, not_found, conflict, …) in your business logic; only show err.message to end users.