Skip to content

[Feature]: Standardize Server OpenAPI Interface Format #59

Description

@me0106

Feature Request: Standardize Server OpenAPI Interface Format

Background

memind-server currently exposes /open/v1/* and /admin/v1/* APIs. Most endpoints already use shared response
models such as ApiResult<T> and PageResult<T>, but the API format is not yet formally standardized across response
structure, pagination, error codes, timestamps, and OpenAPI documentation.

This makes it harder for the UI, Java client, Python client, and future SDKs to rely on a stable API contract.

Goal

Define and enforce a consistent OpenAPI interface format for memind-server.

Scope

The standard should cover:

  • Unified success response format
  • Unified error response format
  • Standard error codes and HTTP status mappings
  • Pagination response format
  • Request and response field naming conventions
  • OpenAPI schema annotations and examples
  • Versioning rules for /open/v1/* and /admin/v1/*
  • Contract expectations for generated clients and frontend consumers

Acceptance Criteria

  • Add an API format document, for example docs/server-api-format.md
  • Document the meaning of ApiResult and PageResult
  • Define standard success, error, empty result, and pagination responses
  • Define stable error codes and HTTP status mappings
  • Ensure /open/v1/* endpoints follow the standard
  • Ensure /admin/v1/* endpoints follow the standard or provide a migration checklist
  • Ensure generated OpenAPI docs correctly describe response and error schemas
  • Ensure UI, Java client, and Python client can parse responses consistently
  • Add tests or contract checks to prevent new endpoints from diverging from the standard

Non-goals

  • Refactoring business logic
  • Changing existing API semantics
  • Introducing authentication or gateway features
  • Removing backward-compatible fields without a migration plan

Expand to show specification

Memind Server API Format

Status: proposed v1 contract for memind-server.

This document defines the HTTP and OpenAPI contract for all
/open/v1/* and /admin/v1/* endpoints. It is the API shape that UI,
generated clients, Java clients, Python integrations, and future SDKs
should rely on.

Goals

  • Provide one response format for success responses.
  • Provide one response format for error responses.
  • Keep HTTP status codes and Memind error codes separate.
  • Standardize pagination, timestamps, request IDs, and field naming.
  • Make async endpoints explicit about accepted work versus completed work.
  • Make generated OpenAPI clients predictable.

Scope

This standard applies to every JSON endpoint under:

  • /open/v1/*
  • /admin/v1/*

It does not define authentication, gateway behavior, or business logic
semantics.

Core Rules

  1. HTTP status codes describe transport, protocol, and resource outcome.
  2. Memind error codes describe domain or application error semantics.
  3. Success responses do not contain a business code.
  4. Error responses contain error.code; this value is not an HTTP status.
  5. JSON API endpoints should not use 204 No Content; return a standard
    success envelope with data: null instead.
  6. Every response includes meta.requestId and meta.timestamp.
  7. If the request includes X-Request-Id, the server echoes it in
    meta.requestId and the response X-Request-Id header. Otherwise the
    server generates one.

Response Envelopes

ApiResult

ApiResult<T> is the success envelope. It means the HTTP request succeeded
and the response body contains the requested result.

{
  "data": {
    "status": "UP",
    "service": "memind-server"
  },
  "meta": {
    "requestId": "018f0d67-8a2d-7a8a-bb70-3f50ddde27f5",
    "timestamp": "2026-05-11T08:00:00Z"
  }
}

For commands that complete successfully but have no result:

{
  "data": null,
  "meta": {
    "requestId": "018f0d67-8a2d-7a8a-bb70-3f50ddde27f5",
    "timestamp": "2026-05-11T08:00:00Z"
  }
}

ApiErrorResult

ApiErrorResult is the error envelope. It is used with non-2xx HTTP status
codes.

{
  "error": {
    "code": "validation_failed",
    "message": "Request validation failed",
    "details": {
      "fieldErrors": {
        "userId": "must not be blank"
      }
    }
  },
  "meta": {
    "requestId": "018f0d67-8a2d-7a8a-bb70-3f50ddde27f5",
    "timestamp": "2026-05-11T08:00:00Z"
  }
}

error.details is optional. It should contain structured machine-readable
details when available, such as validation field errors.

Pagination

List endpoints return ApiResult<PageResult<T>>.

Request parameters:

  • page: 1-based page number. Default: 1.
  • pageSize: number of items per page. Default: endpoint-specific, normally
    20. Maximum: 100 unless the endpoint documents a smaller limit.

Response:

{
  "data": {
    "items": [],
    "page": {
      "page": 1,
      "pageSize": 20,
      "totalItems": 123,
      "totalPages": 7,
      "hasPrevious": false,
      "hasNext": true
    }
  },
  "meta": {
    "requestId": "018f0d67-8a2d-7a8a-bb70-3f50ddde27f5",
    "timestamp": "2026-05-11T08:00:00Z"
  }
}

PageResult<T> means:

  • items: the current page contents.
  • page.page: the current 1-based page number.
  • page.pageSize: requested page size after server validation.
  • page.totalItems: total number of matching items.
  • page.totalPages: ceil(totalItems / pageSize).
  • page.hasPrevious: whether a previous page exists.
  • page.hasNext: whether a next page exists.

Async Operations

Endpoints that complete work before responding return 200 OK.

Endpoints that accept work for later processing return 202 Accepted with an
operation result. 202 Accepted means the server accepted or durably queued
the request. It does not mean the business operation completed.

{
  "data": {
    "operationId": "op_01HX8Y3V4E8ZJ9N7M6Q5P4R3T2",
    "status": "accepted",
    "mode": "async"
  },
  "meta": {
    "requestId": "018f0d67-8a2d-7a8a-bb70-3f50ddde27f5",
    "timestamp": "2026-05-11T08:00:00Z"
  }
}

Fire-and-forget endpoints that only schedule in-process work must not return
success unless the request has been persisted or otherwise made retry-safe.
Retry-aware clients must be able to distinguish accepted work from completed
work.

Standard HTTP Status and Error Codes

HTTP status Error code Meaning
400 bad_request Request cannot be processed because its shape or parameters are invalid.
400 validation_failed Bean validation or semantic request validation failed.
400 malformed_json Request body is not valid JSON or cannot be decoded.
404 not_found Requested resource does not exist.
409 conflict Request conflicts with current server state.
409 version_conflict Optimistic locking or expected version check failed.
415 unsupported_media_type Request content type is not supported.
429 rate_limited Caller exceeded an enforced rate limit. Reserved until rate limiting exists.
500 internal_error Unexpected server failure.
503 runtime_unavailable Memory runtime is not ready or cannot serve requests.
503 dependency_unavailable Required external dependency is unavailable.

Rules:

  • Do not use numeric strings such as "200" or "500" as Memind error
    codes.
  • Do not return 200 OK with an error envelope.
  • Do not return a success envelope for failed async work.
  • Use the most specific code available.
  • internal_error is only for unexpected server bugs or uncategorized
    failures.

Naming Conventions

  • JSON fields use camelCase.
  • Query parameters use camelCase.
  • Resource identifiers use the domain suffix: userId, agentId,
    rawDataId, itemId, insightId, threadKey.
  • Collection fields use plural names, normally items.
  • Paginated collections always use data.items, never data.list.
  • Timestamps are ISO-8601 strings in UTC, for example
    2026-05-11T08:00:00Z.
  • Public enum values should use lower_snake_case. If internal Java enums use
    UPPER_SNAKE_CASE, translate them at the API boundary.
  • Optional fields may be omitted when unset. Required nullable fields must be
    marked as nullable in OpenAPI.

OpenAPI Requirements

Every operation must define:

  • operationId
  • tags
  • summary
  • Request schema, if the operation accepts a body.
  • Query and path parameters with validation limits.
  • One success response schema and example.
  • Common error responses: 400, 404 where applicable, 409 where
    applicable, 500, and 503.

Reusable schemas should be declared once under components.schemas.

components:
  schemas:
    ResponseMeta:
      type: object
      required: [requestId, timestamp]
      properties:
        requestId:
          type: string
        timestamp:
          type: string
          format: date-time

    ApiResult:
      type: object
      required: [data, meta]
      properties:
        data:
          nullable: true
        meta:
          $ref: '#/components/schemas/ResponseMeta'

    ApiError:
      type: object
      required: [code, message]
      properties:
        code:
          type: string
        message:
          type: string
        details:
          type: object
          additionalProperties: true

    ApiErrorResult:
      type: object
      required: [error, meta]
      properties:
        error:
          $ref: '#/components/schemas/ApiError'
        meta:
          $ref: '#/components/schemas/ResponseMeta'

    PageMeta:
      type: object
      required: [page, pageSize, totalItems, totalPages, hasPrevious, hasNext]
      properties:
        page:
          type: integer
          minimum: 1
        pageSize:
          type: integer
          minimum: 1
          maximum: 100
        totalItems:
          type: integer
          format: int64
          minimum: 0
        totalPages:
          type: integer
          minimum: 0
        hasPrevious:
          type: boolean
        hasNext:
          type: boolean

    PageResult:
      type: object
      required: [items, page]
      properties:
        items:
          type: array
          items: {}
        page:
          $ref: '#/components/schemas/PageMeta'

    OperationAccepted:
      type: object
      required: [operationId, status, mode]
      properties:
        operationId:
          type: string
        status:
          type: string
          enum: [accepted]
        mode:
          type: string
          enum: [async]

OpenAPI does not support Java-style generics directly. For each endpoint,
compose ApiResult with the concrete data schema in the operation response.
For example, GET /open/v1/health should document data as
OpenMemoryHealth.

Polymorphic request or response types must use oneOf with a discriminator.
This is required for raw content, content blocks, and source payloads.

Client Contract

Clients must:

  • Treat 2xx HTTP responses as successful transport outcomes.
  • Parse successful responses from data.
  • Treat 202 Accepted as accepted asynchronous work, not completed work.
  • Treat non-2xx responses as errors and parse error.
  • Preserve and surface meta.requestId for diagnostics.

Clients must not:

  • Decide success by comparing a body field to "success" or "200".
  • Expect error details in data.
  • Retry all failures blindly. Retry policy should use HTTP status and
    endpoint semantics.

Recommended retry behavior:

  • Retry transport failures, timeouts, 429, and 503 when the operation is
    idempotent or has an idempotency key.
  • Do not retry 400, 404, or validation failures without changing the
    request.
  • Do not mark async write operations as completed after 202 Accepted.

Versioning

  • The major API version is encoded in the URL: /open/v1 and /admin/v1.
  • Backward-incompatible wire changes require a new major URL version.
  • Additive fields are allowed in the same version.
  • Removing fields, renaming fields, changing field types, or changing enum
    values is breaking.
  • Deprecated fields must be marked with deprecated: true in OpenAPI and kept
    until the next major API version.
  • /open/v1/* is a public client contract.
  • /admin/v1/* is primarily for the UI, but it must still follow this
    document so generated clients and contract tests remain reliable.

Endpoint Examples

Health

GET /open/v1/health
{
  "data": {
    "status": "up",
    "service": "memind-server"
  },
  "meta": {
    "requestId": "018f0d67-8a2d-7a8a-bb70-3f50ddde27f5",
    "timestamp": "2026-05-11T08:00:00Z"
  }
}

Retrieve Memory

POST /open/v1/memory/retrieve
{
  "userId": "user-1",
  "agentId": "agent-1",
  "query": "What does the user prefer for backend architecture?",
  "strategy": "simple",
  "trace": false
}
{
  "data": {
    "status": "completed",
    "items": [],
    "insights": [],
    "rawData": [],
    "evidences": [],
    "strategy": "simple",
    "query": "What does the user prefer for backend architecture?",
    "trace": null
  },
  "meta": {
    "requestId": "018f0d67-8a2d-7a8a-bb70-3f50ddde27f5",
    "timestamp": "2026-05-11T08:00:00Z"
  }
}

Admin Page

GET /admin/v1/items?page=1&pageSize=20&userId=user-1
{
  "data": {
    "items": [],
    "page": {
      "page": 1,
      "pageSize": 20,
      "totalItems": 0,
      "totalPages": 0,
      "hasPrevious": false,
      "hasNext": false
    }
  },
  "meta": {
    "requestId": "018f0d67-8a2d-7a8a-bb70-3f50ddde27f5",
    "timestamp": "2026-05-11T08:00:00Z"
  }
}

Migration Checklist

Use this checklist when implementing the standard:

  • Replace success response bodies with ApiResult<T> using data and meta.
  • Replace error response bodies with ApiErrorResult.
  • Remove success business codes such as "success" and "200" from response
    bodies.
  • Move validation details from data to error.details.
  • Change pagination from total/list/current to items/page.
  • Change list query parameters from pageNo to page.
  • Add request ID generation and response header echoing.
  • Make async write endpoints return 202 Accepted only after durable
    acceptance, or make them synchronous and return 200 OK only after
    completion.
  • Add OpenAPI schemas, examples, and common error responses.
  • Update UI and SDK clients to rely on HTTP status and the new envelopes.

Contract Checks

The server should include automated checks that prevent new endpoints from
drifting from this standard:

  • Controller return types are ApiResult<T>,
    ResponseEntity<ApiResult<T>>, or ResponseEntity<ApiErrorResult>.
  • All exception handlers produce ApiErrorResult.
  • OpenAPI output contains the common response schemas.
  • Every operation documents at least one 2xx response and the common error
    responses.
  • Paginated endpoints use PageResult<T>.
  • Error codes in OpenAPI match the standard error code list.
  • Async endpoints never document 200 OK unless the work completes before the
    response is sent.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions