The Claude API (/_claude/*)

An internal REST surface on the 4D side, meant for an automated agent: read and write blocks, run a host method, inspect Storage, reload the project. It is how a page is edited programmatically — never by hand-editing an export file.

Calling it

curl -sk -X POST "https://127.0.0.1:<port>/_claude/ping" \
  -H "Host: <domain>" -H "Content-Type: application/json" -d '{}'

Four things that are not optional:

  • curl, not a tool that caches responses. A cached answer from a previous state is worse than no answer.
  • A body, even empty (-d '{}'). Without it 4D answers 411 Length Required.
  • The Host header whenever you target 127.0.0.1, so the domain resolves.
  • -k on the self-signed certificate. On Windows, PowerShell's Invoke-WebRequest fails against it whatever you try; use curl.exe from System32.

Answers are sometimes base64, occasionally nested twice:

… | sed 's/^base64://' | base64 -d | sed 's/^base64://' | base64 -d

Send accented values from a file, not inline. An accented vt_FieldLabel written straight into a -d '{…}' payload comes back corrupted. Use curl --data-binary @payload.json -H "Content-Type: application/json; charset=utf-8".

Authorisation

A remote call requires a token, generated with ClaudeAPI.generateToken($minutes). The token lives in Storage: there is only ever one, and a 4D restart loses it.

Warning: the token opens exec and the whole Storage, so on a public site it is the keys to the house. Keep its lifetime short and revoke it. Do not expose /_claude to the Internet.

Routes

RouteWhat it does
pingLiveness, and tells you the build
execRuns a host method through BSPH_EXECUTE_METHOD
reloadRELOAD PROJECT — host code only
storageReads Storage, or refreshes it
data/<TABLE>/<action>ORDA access, below
admin/get|setReads or writes the encrypted admin templates by slug

exec takes no parameters

It calls a host method by name and returns its Text result, if it has one. There is no way to pass arguments — a method that needs input has to read it from elsewhere. And never run a method that calls ALERT on a server: the dialog blocks the process with nobody to click it.

data actions

Read: query, get, all, first, validate, render-fields, render-style, page-tree, template-tree, export-template, files-integrity, migration-report.

Write: create, update, delete, set-property, place-template, import-template, capture-template, migration-apply, migration-files.

Writing is restricted by a whitelist, claude-api-write-tables.json, at the root of the host project. A table missing from it answers Write not allowed for table: … Check claude-api-write-tables.json — that is a configuration answer, not a bug.

There is no sort

Pagination is limit (default 20) and offset. There is no orderBy, and all returns records in storage order. Asking for "the most recent" with limit=1 therefore returns an arbitrary record — an easy way to draw conclusions from an old state. Page through and sort client-side.

Traps worth knowing before you write

  • admin/get reads memory, not disk. The template it returns comes from Storage. If a dynamic function corrupted it in place, a get followed by a set writes that corruption to disk permanently. Work on a .copy().
  • Never pre-generate uuids. Create the parent, read the uuidKey the API returns, then use it as the children's parentUuid. A parentUuid of 32 spaces gives an empty page.
  • Object fields must be {}, never null — properties, cssProperties, events. A null there has already crashed CSS generation at startup.
  • Creating blocks through the API does not extract Tailwind classes. The page renders unstyled until the class list is rebuilt. See Styling: the CSS cube and Tailwind.
  • Long text values (vt_Content) travel base64-encoded.

See also