Skip to content

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..." }
}
  • metadata carries non-resource context (organization scope, project scope where applicable, server timestamp).
  • data carries 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 metadata without 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 }
    }
  ]
}