Skip to main content

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 read in 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 example programmes, 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:

HTTPCodeMeaning
400VALIDATION_ERRORMissing or invalid tenant header, invalid limit or cursor.
401UNAUTHENTICATEDNo session.
403MFA_REQUIREDTenant requires MFA enrolment and the account has none.
403FORBIDDENNo active role in the tenant, or the resource's role list excludes the actor.
404NOT_FOUNDUnknown resource path.
501FEATURE_UNAVAILABLEResource is gated (currently benchmarks).

Side effects: none.

POST /api/v1/{resource}​

  • Purpose. Creates a record. Supported only for the nine resources marked crud in 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:

KindRule
stringTrimmed; max length as configured
code^[A-Z0-9][A-Z0-9._/-]{1,63}$
uuidUUID
dateYYYY-MM-DD
datetimeISO 8601
integerInteger with configured bounds
enumOne of the listed values
booleantrue or false
jsonAny 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:

HTTPCodeMeaning
400VALIDATION_ERRORField errors (fields), invalid JSON, body not an object, origin missing or not allowed (this helper returns 400), unknown field.
400VALIDATION_ERRORSecret-like key names in the body (for example password, token, apiKey) are rejected with a message to store secrets in the deployment secret store.
401UNAUTHENTICATEDNo session.
403MFA_REQUIRED / FORBIDDENAs for GET.
409CONFLICTUnique constraint (for example, duplicate code).
501FEATURE_UNAVAILABLEResource 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 crud resources as POST.
  • Body. id (UUID, required) plus any fields in the resource's update list. id is 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.