type: "http") calls an external API and stores the response on the case. With the optional mapping, parts of the response are written into other case fields — credit scores, company data verification, address lookups, vehicle registrations, or any external data source with an HTTPS API.
A team member runs the call from the case view (the Run button on the field); the response and its status are stored on the field itself, and mapped values land in the target fields immediately.
When to Use
Use API Call fields for:- Credit score checks
- Company data verification (registration, VAT validation)
- Address validation and geocoding
- Bank account verification (IBAN validation)
- Vehicle registration lookups
- API-based enrichment services
- Use Text, Number, or other fields for manual data entry
- Use case automations (
httpaction) to call an API automatically when case data changes — the API Call field itself only runs on demand - Use webhooks for pushing data to external systems
Configuration
A field entry in the case schema:options
Every option value may contain expressions resolved against the case at run time:
{data.vat_number}, {contacts.main.email}, and company secrets via {$secrets.my_api_key} (never hardcode credentials).
Unknown option keys are rejected. Adding an option Penbox does not know — response_mapping, api_config, trigger — fails with a validation error naming the key and pointing at it. The check fires only on keys you add: a configuration that already carries a legacy key stays savable, so an unrelated edit never bricks an existing case. A few legacy aliases (uri, json, responseType) are also still accepted for configurations that use them.
Response Mapping
Without amapping, the response is only stored on the API Call field itself. With one, parts of the response are also written into other case fields — visible in the case immediately, recorded in the case history, and able to trigger automations like any other field update.
Inside mapping values, the parsed response body is available as @payload:
- Targets must be existing field keys in the case schema. The mapping writes values; it never creates fields. Unwritable targets are skipped (
mapping_skipped), while valid targets are still written. - Two equivalent shapes: nested
{ "data": { "field": … } }or flat{ "data.field": … }. - Values are expressions — they can read
@payloadbut also any case data ({data.other_field}). { "value": …, "replace": false }skips the write when the target already holds a non-empty value. This is the same vocabulary as the form API element’smapping.- A mapping value pointing at a missing response path writes nothing and is reported as unresolved (
mapping_unresolved) — a mistyped@payloadpath is the most common cause of “the API call worked but the case stayed empty”.
Mapping error responses
APIs often return useful bodies on failure (e.g. validation errors with a400). The response body is stored even when the call fails, so @payload works on the error path too. If you want non-2xx statuses treated as normal responses instead of errors, set "responseMode": "full".
Per-field error messages
When an API rejects one of the values you sent, it answers with a message about that value. Theerrors root puts that message under the field it is about, instead of leaving it inside the stored response:
chassisnummer row then shows the message in red underneath it, and the field’s status reads 1 rejected.
Rules:
- Same target rules as
data: the key must already exist in the case schema, and it cannot be the API Call field’s own key. Bad targets are reported inmapping_skippedunder the name you wrote (errors.not_a_field). errorsmust be an object to mean the error root."errors": "{@payload.x}"still means “write into a case field literally keyederrors” — existing configurations are unaffected.- Values are expressions, and each must resolve to a string or to a list of strings (joined with
,). - A path that resolves to nothing produces no message. This is the normal case: the same mapping serves the run where the API rejects the field and the run where it accepts it. Unlike a value entry, an unresolved error entry is not counted as unresolved and does not affect the field’s status.
- No
{ "value": …, "replace": false }wrapper here — an error message is always the last word on the field.
field_errors, so they never appear in the case history, never trigger automations, and are never served by the public API as if they were data about the dossier. The next run replaces them wholesale, and a run that rejects nothing clears them. A message also disappears as soon as a team member edits the row it sits under, because it describes the value that was just replaced.
Stored value
After a run, the field holds:error carries the HTTP error name on failure. The mapping keys only appear when there is something to report: mapping_written names the fields that were filled, mapping_skipped the targets that do not exist, mapping_unresolved the entries whose value expression resolved to nothing, and mapping_error a mapping that could not be evaluated at all. Click the field’s status in the case view to inspect this payload.
field_errors is the odd one out: it is not a diagnostic about your configuration but the messages the API returned about the case’s data, written by the errors mapping root. It is keyed by field, and it is the only place those messages exist.
Editing in the app
In the case view, the field’s menu offers Edit API Call: method, URL, headers, body, and the response mapping as JSON. Options managed outside the modal (e.g.responseMode, params) are preserved when saving.
Examples
Vehicle registration push
Send case data to a registration service and capture returned errors on the case:Company data enrichment
Best Practices
- Declare the target fields first — the mapping only writes into fields that already exist in the case schema.
- Use
{$secrets.…}for credentials — never paste API keys into the configuration. - Use
replace: falsefor fields a team member may have filled manually and that the API should not overwrite. - Read the ignored count on the field when a target stays empty — it names the entries that could not be written and why, so a mistyped
@payloadpath is visible instead of silent.