API Action Types
The four kinds of action an endpoint can perform, and how their configuration fields are described.
The Four Action Types
Every action is one of four types. Choosing the right one is most of designing a custom endpoint well.
Where to find it
Architect Panel → Integration & Connections:
- API Server — where actions are configured
Architect Panel → Data:
- Datastores — what the fetch actions read
- Data Extraction — the ingestion pipeline the fourth type hands to
Fetch a row
Returns a single record. The right choice when the caller knows which one they want — a lookup by reference, a status check.
Fetch all rows in a table
Returns a set. Useful for reference data and small collections.
Be careful with it on anything large. A caller fetching an entire datastore on every request is a pattern that works in testing and becomes a problem at volume, and it is the commonest cause of an integration that gets slower as your data grows. Where a caller needs a subset, giving them a narrower endpoint is kinder than letting them filter client-side.
Run a PHP callback function
Hands control to your own code. The most capable option and the one to justify.
It is right when the operation is genuinely logic rather than data movement — a calculation, a decision, a call out to something else. It is wrong as a default: code in a callback is invisible to everybody reading the configuration, so business rules that live there are rules nobody will find.
If you can express something as configuration instead, do.
Perform data extraction
Passes the payload to an extraction definition, which maps and transforms it into a datastore.
This is the best answer for "a partner posts us structured data". The mapping lives in the extraction definition where it is visible and adjustable, the formats it accepts are already handled, and every run is logged. Building the same thing as a callback means reimplementing all of that in code.
Choosing
- Returning one known record — fetch a row.
- Returning a small, bounded set — fetch all rows.
- Receiving structured data to store — extraction.
- Anything genuinely computational — callback.
Prefer configuration to code
The general principle here. The first, second and fourth types are configuration: visible, reviewable, and changeable without a deployment. The third is code. Reach for it when the others genuinely cannot do the job, not because it is the most flexible.
Each type has its own settings
An action type declares the fields it needs, so the form you fill in matches the type you chose rather than being a generic catch-all. Required fields are marked, and anything sensitive is stored encrypted.
Worked example
A partner integration uses three actions across two endpoints: extraction to receive their nightly referrals file, fetch-a-row so they can check a referral's status by reference, and one callback that computes an eligibility score — the only genuinely computational step, and the only one that needed code.
Recommendations
- Use extraction for inbound structured data, not a callback.
- Avoid fetch-all on large datastores — give a narrower endpoint.
- Justify every callback.
- Prefer configuration, because it can be reviewed.
Field Mappings and Defaults
An action needs to know which fields it reads or writes, and what to do when the caller supplies nothing.
Where to find it
Architect Panel → Integration & Connections:
- API Server — the action and its field configuration
- OpenAPI Specification — what the caller is told to send
Architect Panel → Data:
- Datastores — the fields being mapped, and their own validation
What a mapping does
It connects what the caller sends to the field it belongs in. Where the two use different names — and they usually do, because the caller's vocabulary is theirs — the mapping is where that translation lives.
Keep your field names, not theirs
Resist renaming a datastore field to match a partner's API. The mapping exists so their vocabulary stays at the boundary; letting it into your data model means the second partner's names conflict with the first's.
Defaults
A default supplies a value when the caller does not. Useful for fields your process needs but the caller has no reason to know about — a source marker, a status, a channel.
They are also how you keep an API stable while your model grows. Adding a required field to a datastore would break every existing caller; adding it with a sensible default does not.
Required is a promise to the caller
Mark a field required when the operation genuinely cannot proceed without it. That produces a clear rejection at the boundary, which is far better than accepting the call and failing further in — the caller can see what they missed and fix it themselves.
Do not mark something required and then also give it a default; the two express opposite intentions and the result confuses everybody reading the configuration.
Validation still applies
The datastore's own field validation runs regardless of what the API does. An action can accept a value the field will then reject, so if a caller reports mysterious failures, check the field's rules and not only the mapping.
Sensitive values are encrypted
Where an action's configuration includes something secret, it is stored encrypted rather than in plain text — the same treatment credentials get elsewhere.
When a value does not arrive
Work through it in this order, which is roughly by likelihood:
- Is the caller actually sending it? Check the inbound request log before anything else.
- Is the mapping pointing at the right field?
- Is a default silently overwriting what they sent?
- Is the field's own validation rejecting the value?
The third catches people out most, because everything looks correctly configured and the value quietly disappears.
Publish what you expect
Give integrators the field list, which are required, what the defaults are and what validation applies — most of which the OpenAPI specification carries. An integrator guessing at your expectations will guess wrong, and you will spend the difference in support.
Worked example
A referral endpoint maps a partner's ref_id to the internal reference and dob to date of birth, defaults the source to that partner and the status to "new", and marks the reference and surname required. When the partner later adds a middle name field, nothing breaks — it is simply unmapped and ignored until somebody decides to use it.
Recommendations
- Translate at the boundary — keep your own field names.
- Default the things callers should not know about.
- Never mark a field both required and defaulted.
- Check for a default overwriting when a value goes missing.