Response envelope¶
Every JSON response from the b'nerd HQ API wraps the resource (or list of resources) in a consistent envelope:
{
"metadata": {
"organization_id": "...",
"project_id": "...",
"timestamp": "2026-05-30T12:34:56Z"
},
"data": { "...resource fields..." }
}
metadatacarries non-resource context (organization scope, project scope where applicable, server timestamp).datacarries the resource itself — either a single object or an array, depending on the endpoint.
List responses¶
List endpoints add metadata.count:
{
"metadata": {
"organization_id": "...",
"project_id": "...",
"timestamp": "...",
"count": 5
},
"data": [ {}, {}, {}, {}, {} ]
}
Empty responses¶
DELETE endpoints (and other endpoints with no resource to return) respond 204 No Content with no body — no envelope.
Errors¶
Errors use a separate ErrorEnvelope shape:
{
"request_id": "01970000-...",
"errors": [
{
"code": "validation_failed",
"message": "name is required",
"status": 422,
"field": "name",
"source": { "service": "openstack", "operation": "create_server" },
"meta": { "retryable": false }
}
]
}
Always check the HTTP status code first; on 4xx/5xx, parse the errors array. errors[0].message is the human-readable message; errors[0].code is the machine-readable code; errors[0].meta.retryable tells you whether a retry might succeed.
Why a universal envelope?¶
Single, predictable shape across every endpoint:
- Trivial client unmarshalling — one struct/type works for any resource.
- Future-proof — pagination cursors, deprecation markers, etc. can be added under
metadatawithout breaking existing callers. - Honest — every response carries server-side context (timestamp, scope).
Example — single resource¶
GET /cloud/servers/srv-1?project_id=proj-1 HTTP/1.1
Authorization: Bearer <token>
HTTP/1.1 200 OK
Content-Type: application/json
{
"metadata": {
"organization_id": "org-1",
"project_id": "proj-1",
"timestamp": "2026-05-30T12:34:56Z"
},
"data": { "id": "srv-1", "name": "web1", "status": "ACTIVE" }
}
Example — list¶
GET /cloud/servers?project_id=proj-1 HTTP/1.1
Authorization: Bearer <token>
HTTP/1.1 200 OK
Content-Type: application/json
{
"metadata": {
"organization_id": "org-1",
"project_id": "proj-1",
"timestamp": "2026-05-30T12:34:56Z",
"count": 2
},
"data": [
{ "id": "srv-1", "name": "web1", "status": "ACTIVE" },
{ "id": "srv-2", "name": "web2", "status": "ACTIVE" }
]
}
Example — error¶
POST /cloud/servers?project_id=proj-1 HTTP/1.1
Content-Type: application/json
{}
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"request_id": "0197abc-...",
"errors": [
{
"code": "missing_parameter",
"message": "param is missing or the value is empty: name",
"status": 400,
"meta": { "retryable": false }
}
]
}