On this page

Administration

Project mode

Read a project's mode with GET /v1/projects/{projectId}/mode and set it with PUT on the same path. Any project-scoped credential can read; writing needs an administrator role.

curl -sS -H "Authorization: Bearer $FABRIC_API_KEY" \
  https://fabric.wexa.ai/v1/projects/proj_abc/mode
{ "project_id": "proj_abc", "mode": "auto", "ui_label": "Simple" }
curl -sS -X PUT https://fabric.wexa.ai/v1/projects/proj_abc/mode \
  -H "Authorization: Bearer $FABRIC_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"manual"}'
{ "project_id": "proj_abc", "mode": "manual", "ui_label": "Advanced" }

Parameters

ParameterTypeRequiredNotes
projectIdstring, in the pathYesMust be the project the credential is scoped to; another project's id is refused (403, project outside token scope).
modestring, in the PUT bodyYes, on PUTauto or simple stores auto (Simple); manual or advanced stores manual (Advanced). Lower case only. Any other value is not rejected: it is stored as auto and answered with 200.
Errors
StatusCodeRaised when
403forbiddenA PUT comes from a caller without an administrator role (changing project mode requires an admin role). The scope check comes first.
400invalid_requestThe PUT body is not JSON.
More details
GET /v1/projects/{projectId}/mode
PUT /v1/projects/{projectId}/mode

For when and how to use it, read Project mode.

Two states, four names

There are exactly two states. Each has two accepted spellings, and the two spellings for a state are synonyms rather than variants:

SendStored asShown in the console as
autoautoSimple
simpleautoSimple
manualmanualAdvanced
advancedmanualAdvanced

GET /v1/projects/{projectId}/mode

curl -sS -H "Authorization: Bearer $FABRIC_API_KEY" \
  https://fabric.wexa.ai/v1/projects/proj_abc/mode
{ "project_id": "proj_abc", "mode": "auto", "ui_label": "Simple" }

Any project-scoped credential can read it — no admin role required. The {projectId} in the path must be the one the credential is scoped to; another project's id is refused rather than answered:

{ "error": "forbidden", "error_description": "project outside token scope" }

The gateway reads the mode through to the platform, the source of truth, with a short cache. If that is briefly unreachable it falls back to the last value this process saw, or the configured default — so a tool call never fails just because the mode could not be re-read.

PUT /v1/projects/{projectId}/mode

curl -sS -X PUT https://fabric.wexa.ai/v1/projects/proj_abc/mode \
  -H "Authorization: Bearer $FABRIC_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"manual"}'
{ "project_id": "proj_abc", "mode": "manual", "ui_label": "Advanced" }

The response is the same shape as the read, reflecting the new state, and a subsequent GET returned the same thing — the write is applied immediately, not eventually.

Writing requires an admin role on the project. A project-scoped member is refused:

{ "error": "forbidden", "error_description": "changing project mode requires an admin role" }

The scope check comes first, so another project's id is refused before the role is even considered.

A successful write is recorded in the audit stream as project.mode.changed, with the new mode in the payload — so "who put this project into Simple mode, and when" is answerable from GET /v1/audit.

The failure that looks like a success

A malformed body, one that is not JSON at all, is a real 400:

{ "error": "invalid_request", "error_description": "…" }

Any other unrecognised value is not an error. It is normalised to auto and answered with 200, as the Advanced row of the ledger below shows:

{ "project_id": "proj_abc", "mode": "auto", "ui_label": "Simple" }

A safe write

Because a wrong value is indistinguishable from a right one by status code alone, the write is worth wrapping in a check:

want=manual
got=$(curl -sS -X PUT "$FABRIC_URL/v1/projects/$PROJECT/mode" \
        -H "Authorization: Bearer $FABRIC_ADMIN_TOKEN" \
        -H "Content-Type: application/json" \
        -d "{\"mode\":\"$want\"}" | jq -r .mode)

[ "$got" = "$want" ] || { echo "mode is '$got', asked for '$want'" >&2; exit 1; }

Note that this compares against the stored spelling. Asking for advanced stores manual, so compare against manual — or compare ui_label against Advanced, which is stable across both spellings.

Per-route ledger

CaseResult
GET on the token's own project200, auto / Simple
GET on another project403 project outside token scope
PUT {"mode":"manual"}200, manual / Advanced; GET confirmed
PUT {"mode":"advanced"}200, stored as manual / Advanced
PUT {"mode":"simple"}200, stored as auto / Simple
PUT {"mode":"Advanced"}200, silently stored as auto / Simple
PUT on another project403 project outside token scope

Where the platform cannot persist the mode, the gateway holds it in its own store instead, so a restart returns the project to Simple.

Every tool call works the same way: How every tool call works.