How to approve or reject a validation step, and how to resubmit corrected data.
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
A Validation step pauses the workfloo execution and waits for a person. It never advances on its own. It alternates between two states until a reviewer approves:
| State | What it means | Who acts | Endpoint |
|---|---|---|---|
REVIEW | Waiting for the reviewer's decision | Internal reviewer | POST /api/v1/workfloo/{id}/review |
CORRECTION | Rejected: there are items to recapture | Whoever captures the data | POST /api/v1/workfloo/{id}/correction |
The execution status stays PROGRESS throughout the cycle. To find out where it currently stands, call GET /api/v1/workfloo/status/{id}:
currentNodeType | Step state |
|---|---|
VALIDATION_PROCESSING | REVIEW — waiting for the reviewer |
VALIDATION (with a validation block) | CORRECTION — waiting for the correction |
Reviewer decision
POST /api/v1/workfloo/{id}/review
Request body
| Field | Type | Required | Description |
|---|---|---|---|
decision | string | Yes | approved or rejected. Any other value returns 400. |
reviewerNote | string | No | Free-form note from the reviewer. Shown in full on the correction step. |
reviews | array | Yes on a rejection (at least 1) | Items to correct. Ignored when approving. |
reviews[].fieldId | string | Yes | Id of the form field or file. This is the key the correction reuses. |
reviews[].sourceNodeId | string | No | Id of the step where the item was captured. For traceability. |
reviews[].message | string | No | Per-item message, visible to whoever corrects it. |
On rejection, the step moves to CORRECTION and the decision is recorded in the execution history. Every round of the cycle is kept: nothing is overwritten.
On approval, the execution advances to the next step, or ends with SUCCESS if the validation step was the last one.
Examples
curl --request POST \
--url https://{your-domain}/api/v1/workfloo/665f1c8e9a2b4c0012ab34cd/review \
--header 'x-api-key: API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"decision": "rejected",
"reviewerNote": "La identificación no permite verificar los datos fiscales.",
"reviews": [
{
"sourceNodeId": "1745012345678",
"fieldId": "rfc_pf",
"message": "El RFC no coincide con el nombre capturado."
},
{
"sourceNodeId": "1745012345678",
"fieldId": "ine_frente",
"message": "La foto está borrosa, vuelve a subirla."
}
]
}'curl --request POST \
--url https://{your-domain}/api/v1/workfloo/665f1c8e9a2b4c0012ab34cd/review \
--header 'x-api-key: API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"decision": "approved",
"reviewerNote": "Documentación correcta."
}'Responses
| Code | When |
|---|---|
200 | Decision applied. No body: confirm the outcome with GET /api/v1/workfloo/status/{id}. |
400 | Invalid decision, rejection without reviews, locked workfloo, current step is not a validation step, or it is not in REVIEW. |
403 | The user does not belong to the reviewer groups assigned to the step. |
404 | Workfloo, version, or current step not found. |
500 | Internal error while persisting. No body. |
Correction
POST /api/v1/workfloo/{id}/correction
Resubmits only the items flagged in the latest rejection. The body is a flat fieldId → value object, just like a form submission. Files are sent as base64, just like in a document step.
curl --request POST \
--url https://{your-domain}/api/v1/workfloo/665f1c8e9a2b4c0012ab34cd/correction \
--header 'x-api-key: API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"rfc_pf": "MOFM900101AB1",
"ine_frente": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ…"
}'To know which items to ask for, read the validation block of GET /api/v1/workfloo/status/{id}:
{
"id": "665f1c8e9a2b4c0012ab34cd",
"name": "Alta de cliente",
"status": "PROGRESS",
"currentNodeType": "VALIDATION",
"currentNodeName": "Revisión de datos",
"currentNodeId": "1745012399999",
"validation": {
"instruction": "Verifica que el RFC coincida con la identificación.",
"reviewerNote": "La identificación no permite verificar los datos fiscales.",
"fields": [
{
"fieldId": "rfc_pf",
"name": "RFC",
"type": "STRING",
"value": "XAXX010101000",
"message": "El RFC no coincide con el nombre capturado."
}
]
}
}Correction rules:
- Form fields are revalidated against their original definition (format, length, catalogs) and are treated as required.
- Files are only checked for presence: sending the base64 is enough.
- Phone numbers travel whole: if the number was flagged for correction, its country code (country_code) must be sent as well.
- Corrected values are stored under the same fieldId and replace the original for every downstream step.
Responses
There are two 400 responses with different shapes
Capture errors arrive as a flat field → message object. State errors arrive in the standard error shape. Tell them apart by the presence of the typeError key.
| Code | When |
|---|---|
200 | Correction accepted. The step goes back to REVIEW and the reviewers are notified. |
400 | Capture errors (field → message map), or invalid state: locked workfloo, step is not a validation step, it is not in CORRECTION, or there are no pending items. |
404 | Workfloo, version, or step not found, or no definition exists for the items to correct. |
500 | Internal error while persisting. No body. |
Full cycle
POST /api/v1/workfloo/{id}/form → initial capture
GET /api/v1/workfloo/status/{id} → "VALIDATION_PROCESSING" (REVIEW)
POST /api/v1/workfloo/{id}/review → {"decision": "rejected", "reviews": [...]}
GET /api/v1/workfloo/status/{id} → "VALIDATION" + validation (CORRECTION)
POST /api/v1/workfloo/{id}/correction → {"rfc_pf": "…"}
GET /api/v1/workfloo/status/{id} → "VALIDATION_PROCESSING" (REVIEW, 2nd round)
POST /api/v1/workfloo/{id}/review → {"decision": "approved"}
GET /api/v1/workfloo/status/{id} → next step, or status "SUCCESS"
The rejection → correction → review cycle can repeat as many times as needed. Every round is recorded.
Integration notes
📘 Where the fieldId values come from
📘 Where the fieldId values come from
There is no endpoint that lists the reviewable fields. You get them from GET /api/v1/workfloo/{id}, under steps[].form.fields and steps[].files. The history of previous decisions lives in steps[].validation.
- The group restriction applies to session users. A call authenticated with x-api-key carries no associated user, so it does not go through the reviewer group filter and reviewedBy is left empty in the history.
- reviews[].kind is not accepted in the request. The backend infers whether an item is a field or a file from the step where it was captured.

