11.4 Generic resources
GET /api/v1/{resource}
- Purpose. Lists records of one resource type, tenant-scoped and ABAC-filtered. The resource list is in Appendix A.
- Authentication. Session cookie.
- Headers.
X-DFFMS-Tenant-Id(required). - Role. Any active role in the tenant for the action
readin the domain policy. Some resources add a resource-specific role list (see Appendix A). Some resources are gated. - Path parameters.
resource: one or more segments (for exampleprogrammes,admin/providers). - Query parameters.
limit(1 to 100, default 25),cursor(UUID).
curl -s "https://dffms.org/api/v1/programmes?limit=10" \
-H 'Cookie: __Secure-dffms.session_token=<session-cookie>' \
-H 'X-DFFMS-Tenant-Id: <tenantId>'
Response (200):
{
"data": {
"items": [
{
"id": "0190f2c4-6b3e-7a1d-8c2f-000000000020",
"tenantId": "<tenantId>",
"code": "AGRO-PRG-01",
"name": "Agro-Input Support Programme",
"status": "ACTIVE",
"createdAt": "2026-10-01T09:00:00.000Z",
"updatedAt": "2026-10-02T10:00:00.000Z"
}
],
"nextCursor": null,
"total": null
},
"error": null,
"requestId": "<request-id>"
}
Errors:
| HTTP | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | Missing or invalid tenant header, invalid limit or cursor. |
| 401 | UNAUTHENTICATED | No session. |
| 403 | MFA_REQUIRED | Tenant requires MFA enrolment and the account has none. |
| 403 | FORBIDDEN | No active role in the tenant, or the resource's role list excludes the actor. |
| 404 | NOT_FOUND | Unknown resource path. |
| 501 | FEATURE_UNAVAILABLE | Resource is gated (currently benchmarks). |
Side effects: none.
POST /api/v1/{resource}
- Purpose. Creates a record. Supported only for the nine resources marked
crudin Appendix A:organizations,programmes,sapzs,institutions,commodities,value-chains,risk-assessments,indicators,reports. - Authentication. Session cookie,
X-DFFMS-Tenant-Id,Origin,Content-Type: application/json. - Role. The domain write permission for that resource kind (see Appendix A), or Platform Super Administrator.
- Body. Fields per resource (see Appendix A for the rules). Unknown fields are rejected with 400
VALIDATION_ERROR. Fields are checked by type and by the rules below.
Field kinds used by the generic validator:
| Kind | Rule |
|---|---|
string | Trimmed; max length as configured |
code | ^[A-Z0-9][A-Z0-9._/-]{1,63}$ |
uuid | UUID |
date | YYYY-MM-DD |
datetime | ISO 8601 |
integer | Integer with configured bounds |
enum | One of the listed values |
boolean | true or false |
json | Any JSON value (object or array) |
Record status enum (status fields): DRAFT, PENDING, APPROVED, REJECTED, ACTIVE, SUSPENDED, CLOSED, ARCHIVED.
Example:
curl -s -X POST https://dffms.org/api/v1/programmes \
-H 'Cookie: __Secure-dffms.session_token=<session-cookie>' \
-H 'X-DFFMS-Tenant-Id: <tenantId>' \
-H 'Origin: https://dffms.org' \
-H 'Content-Type: application/json' \
-d '{
"code": "AGRO-PRG-01",
"name": "Agro-Input Support Programme",
"description": "Input financing for smallholder farmers.",
"startsOn": "2026-01-01",
"endsOn": "2028-12-31",
"status": "DRAFT"
}'
Response (201): the created record, with id, tenantId, cellId, the submitted fields and timestamps. Idempotency-Replayed is never set for generic creates (see Section 6).
Errors:
| HTTP | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | Field errors (fields), invalid JSON, body not an object, origin missing or not allowed (this helper returns 400), unknown field. |
| 400 | VALIDATION_ERROR | Secret-like key names in the body (for example password, token, apiKey) are rejected with a message to store secrets in the deployment secret store. |
| 401 | UNAUTHENTICATED | No session. |
| 403 | MFA_REQUIRED / FORBIDDEN | As for GET. |
| 409 | CONFLICT | Unique constraint (for example, duplicate code). |
| 501 | FEATURE_UNAVAILABLE | Resource is read-only. The role check runs first, so an unauthorised caller gets 403 instead. |
Side effects: writes one audit event with action CREATE, resource type and resource id, and the list of changed field names (values are not stored in the audit event). risk-assessments records the creator as assessedBy and forces status DRAFT.
PATCH /api/v1/{resource}
- Purpose. Updates a record. Same nine
crudresources as POST. - Body.
id(UUID, required) plus any fields in the resource's update list.idis removed before validation. - Rules. At least one mutable field is required. The record must be inside the actor's scope. Missing record: 404.
Example:
curl -s -X PATCH https://dffms.org/api/v1/programmes \
-H 'Cookie: __Secure-dffms.session_token=<session-cookie>' \
-H 'X-DFFMS-Tenant-Id: <tenantId>' \
-H 'Origin: https://dffms.org' \
-H 'Content-Type: application/json' \
-d '{"id":"0190f2c4-6b3e-7a1d-8c2f-000000000020","status":"ACTIVE"}'
Response (200): the updated record. Not found: 404 NOT_FOUND, "<label> was not found".
Errors: as for POST, plus 400 when id is missing or not a UUID, and 400 VALIDATION_ERROR "At least one mutable field is required". No version or ETag check is implemented (last write wins). Audit action is UPDATE with changed field names.
OPTIONS /api/v1/{resource}
The route exports an OPTIONS handler that answers preflight for allowed origins. Allowed methods listed: GET, POST, PATCH, OPTIONS.