/docs/Development/Artifuncs Json
back to app →

artifuncs.json

Every tool repo has an artifuncs.json at its root. It declares the tool's interface: what type of tool it is, what inputs it takes, what outputs it produces, what settings users can tweak, and which external APIs it's allowed to call.

The file is read by the sandbox to render your tool's UI and validate its inputs and outputs. Invalid JSON blocks saving and publishing.

Minimal example

json
{
  "name": "Sum",
  "type": "INPUT_OUTPUT",
  "language": "PYTHON",
  "fields": {
    "input": {
      "a": { "type": "number", "default": 0 },
      "b": { "type": "number", "default": 0 }
    },
    "output": {
      "sum": { "type": "number" }
    }
  }
}

Top-level keys

KeyRequiredDescription
nameyesDisplay name shown on the tool card.
typeyesINPUT_OUTPUT or IFRAME. See below.
languageyesPYTHON.
layoutnoHORIZONTAL (default) or VERTICAL. Controls how input and output panels stack.
fieldsyes (for INPUT_OUTPUT)Input and output field declarations. See Fields.
settingsnoUser-tunable values that persist across runs but aren't part of the per-run input.
runButtonnotrue to show a manual Run button under the form. Hidden by default. See below.
userInstructionsnoFree-text usage guidance shown to visitors above the tool form. See below.
metanoPublic tool metadata — description, seoDescription, tags. Applied when a newer version is approved. See below.
networknoEgress allowlist — the exact hosts (and optionally paths) the tool may reach. See below.

type

  • INPUT_OUTPUT — The default. Your tool receives an input object, returns an output object. Inputs and outputs are rendered as forms from the fields block.
  • IFRAME — Your tool is a self-contained web page rendered in an iframe (URL configured via the General tab of the tool editor). fields are ignored.

The type is locked at tool creation and can't be changed.

fields

Two sub-blocks: input and output. Each maps a field name to a field declaration:

json
"fields": {
  "input": {
    "source": { "type": "text-field", "label": "Source", "default": "" }
  },
  "output": {
    "result": { "type": "text-field", "label": "Result" }
  }
}

The field name (e.g. source) is the key your code reads from input and writes to output. See the Fields reference for every supported type and option.

settings

Same shape as fields.input, but values live in a separate Settings panel and persist between runs. Use settings for things a user tweaks once and reuses (precision, API keys, default formats).

json
"settings": {
  "precision": { "type": "number", "label": "Decimal places", "default": 2 }
}

In your Python code, settings are available as the second argument to process:

python
def process(input, settings):
    return { "sum": round(input["a"] + input["b"], settings["precision"]) }

runButton

Funcs run automatically — an INPUT_OUTPUT func re-runs (debounced) whenever an input changes, and an OUTPUT func runs on load — so no Run button is shown by default.

Set runButton to true when the user needs an explicit trigger: the func has no input fields to drive the auto-run, or a run is expensive enough that you'd rather the visitor ask for it than have every keystroke fire one.

json
{
  "name": "Random Quote",
  "type": "OUTPUT",
  "language": "PYTHON",
  "runButton": true,
  "fields": { "input": {}, "output": { "quote": { "type": "text-field" } } }
}

Only the literal boolean true shows the button; false, a missing key, or any other value hides it. Auto-run behaviour is unchanged either way — the button is an additional trigger, not a replacement.

userInstructions

Optional free-text guidance for people using your tool. When set, it's shown in a "How to use" panel above the tool form on the public func page (/f/<handle>). Use it to explain what the tool expects, how to read the output, or any caveats.

json
{
  "name": "CSV Cleaner",
  "type": "INPUT_OUTPUT",
  "language": "PYTHON",
  "userInstructions": "Upload a CSV with a header row. Empty rows are dropped and columns are trimmed. Large files (>5 MB) may take a few seconds.",
  "fields": { "input": { "...": {} }, "output": { "...": {} } }
}

Line breaks in the string are preserved. It's plain text (not Markdown), so write it as you'd want it read.

meta

Public metadata for the tool as a whole — its listing description, SEO/OG description, and tags. Unlike fields or settings (which belong to a single version), these describe the tool, so they're applied to the public listing when a version is approved and goes live.

json
"meta": {
  "description": "Count and inspect GPT tokens for any text.",
  "seoDescription": "Free online tiktoken tokenizer — token counts and IDs.",
  "tags": ["nlp", "tokenizer", "openai"]
}
FieldDescription
descriptionShort summary shown on the tool card and page.
seoDescriptionSERP / OG description for the public page. Max 200 characters (longer is truncated).
tagsList of tag names. New tags are created automatically; existing ones are reused.

When it's applied. On its own, editing meta and pushing does nothing to the live tool — it only takes effect when a version is approved (goes from review to published). At that point, if the approved version is newer than the currently-published one, its meta is applied to the public tool. Approving an older version (e.g. re-approving v4 while v5 is already live) leaves the metadata untouched — the newest published version always wins.

Each field is independent: omit one to leave it unchanged. An explicit empty tags: [] clears all tags; omitting tags leaves them as they are.

network

Tools run with no outbound network by default. network.allow declares the exact endpoints your tool is permitted to reach — declare only what you actually need, so reviewers (and the people running your tool) can see precisely where it connects.

This includes download_asset() in an init() hook: fetching a model at setup time needs the model's host listed here, plus wherever it redirects to (most model hosts redirect to a CDN).

Note the shape — network is an object with an allow array, not a bare array. A bare array is ignored, which means deny‑all, and the only symptom is a blocked request at runtime.

json
"network": {
  "allow": [
    "api.stripe.com/v1/charges/**",
    "*.googleapis.com/storage/**",
    "api.example.com"
  ]
}

Each entry is host with an optional /path pattern:

  • Host — exact (api.stripe.com) or a *. wildcard that matches subdomains and the bare domain (*.googleapis.com matches storage.googleapis.com and googleapis.com). Hosts are case-insensitive.
  • Path (optional) — narrows the entry to matching request paths. * matches within a single path segment; ** matches across segments. Paths are case-sensitive. An entry with no path allows any path on that host.
DeclaredMeaning
["api.x.com/v1/**"]only api.x.com, only paths under /v1/
["api.x.com"]api.x.com, any path
[] (empty)explicitly no network
key omittedno network (default-deny)

How it's enforced. The host is enforced at the network layer — a tool physically cannot open a connection to a host that isn't declared, no matter how its code tries (this is the guarantee). The path is declared intent: it's shown to reviewers and checked by the provided HTTP client, but because HTTPS traffic is encrypted, path rules are not enforced at the network layer. Keep secrets safe by scoping to the host you trust; use paths to communicate and narrow intent.

Custom (view) funcs — the same allowlist covers the browser. A custom view renders inside a sandboxed, isolated iframe, and the same network.allow list governs what that view's browser-side code (fetch, images, WebSocket, etc.) may reach — enforced by a Content-Security-Policy on the frame. With nothing declared, the view has no outbound network at all (default-deny), which is fine for most views because data flows through ctx.run() / ctx.files (a message to the host, not a direct request). Declare a host here only if your view fetches from it directly in the browser. As at the network layer, enforcement is host-granular: *. wildcards work (*.example.com), but a /path suffix is not enforced for the view — allowing a host allows any path on it in the browser.

Validation

  • artifuncs.json must be valid JSON. The IDE blocks save on parse errors.
  • name, type, language are required.
  • For INPUT_OUTPUT, fields.input and fields.output must exist and be objects (they can be empty {}).
  • Field names must be valid identifiers — letters, digits, and underscores only.
  • Field types must be one of the supported types. Unknown types render as an error placeholder.

Publishing runs the same checks plus a deeper review (see Publishing a tool).