Files
galaxy-game/mail/api/internal-openapi.yaml
T
2026-04-17 18:39:16 +02:00

726 lines
20 KiB
YAML

openapi: 3.0.3
info:
title: Galaxy Mail Service Internal REST API
version: v1
description: |
This specification documents the trusted internal REST contract of
`galaxy/mail`.
The current document freezes:
- the dedicated auth-delivery route used by `Auth / Session Service`;
- the trusted operator read and resend routes used for delivery audit and
recovery.
Contract rules:
- the internal surface lives under `/api/v1/internal`;
- request and response bodies are JSON only;
- auth-delivery intake requires the `Idempotency-Key` header;
- request bodies use strict JSON decoding with unknown-field rejection;
- trailing JSON input is rejected;
- success outcomes are limited to `sent` and `suppressed` on the auth
route;
- mismatched replays on the same `Idempotency-Key` return `409 conflict`;
- operator listing order is `created_at_ms DESC`, then `delivery_id DESC`;
- operator list cursors are opaque base64url encodings of
`created_at_ms:delivery_id`;
- `sent` means durable acceptance into the mail-delivery pipeline rather
than immediate SMTP completion;
- auth callers must not automatically retry transport or upstream
failures;
- `Auth / Session Service` sends the created `challenge_id` as the raw
`Idempotency-Key` header value.
servers:
- url: http://localhost:8080
description: Default local internal listener for Mail Service.
tags:
- name: AuthIntegration
description: Trusted auth-facing mail-delivery intake frozen for `Auth / Session Service`.
- name: OperatorIntegration
description: Trusted operator-facing delivery reads and resend controls.
paths:
/api/v1/internal/login-code-deliveries:
post:
tags:
- AuthIntegration
operationId: acceptLoginCodeDelivery
summary: Accept one auth login-code delivery request
description: |
Validates one trusted auth login-code delivery request and accepts it
durably into the internal mail-delivery pipeline or intentionally
suppresses outward delivery while keeping the auth flow success-shaped.
parameters:
- $ref: "#/components/parameters/IdempotencyKey"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/LoginCodeDeliveryRequest"
responses:
"200":
description: Stable auth-delivery acceptance outcome.
content:
application/json:
schema:
$ref: "#/components/schemas/LoginCodeDeliveryResponse"
"400":
$ref: "#/components/responses/InvalidRequestError"
"409":
$ref: "#/components/responses/ConflictError"
"500":
$ref: "#/components/responses/InternalError"
"503":
$ref: "#/components/responses/ServiceUnavailableError"
/api/v1/internal/deliveries:
get:
tags:
- OperatorIntegration
operationId: listDeliveries
summary: List durable deliveries for trusted operators
description: |
Returns one deterministic page of brief delivery summaries ordered by
`created_at_ms DESC`, then `delivery_id DESC`.
parameters:
- $ref: "#/components/parameters/RecipientFilter"
- $ref: "#/components/parameters/StatusFilter"
- $ref: "#/components/parameters/SourceFilter"
- $ref: "#/components/parameters/TemplateIDFilter"
- $ref: "#/components/parameters/IdempotencyKeyFilter"
- $ref: "#/components/parameters/FromCreatedAtMSFilter"
- $ref: "#/components/parameters/ToCreatedAtMSFilter"
- $ref: "#/components/parameters/ListLimit"
- $ref: "#/components/parameters/ListCursor"
responses:
"200":
description: One deterministic page of delivery summaries.
content:
application/json:
schema:
$ref: "#/components/schemas/DeliveryListResponse"
"400":
$ref: "#/components/responses/InvalidRequestError"
"500":
$ref: "#/components/responses/InternalError"
"503":
$ref: "#/components/responses/ServiceUnavailableError"
/api/v1/internal/deliveries/{delivery_id}:
get:
tags:
- OperatorIntegration
operationId: getDelivery
summary: Get one durable delivery for trusted operators
parameters:
- $ref: "#/components/parameters/DeliveryIDPath"
responses:
"200":
description: One full delivery view with the optional dead-letter entry.
content:
application/json:
schema:
$ref: "#/components/schemas/DeliveryDetailResponse"
"400":
$ref: "#/components/responses/InvalidRequestError"
"404":
$ref: "#/components/responses/DeliveryNotFoundError"
"500":
$ref: "#/components/responses/InternalError"
"503":
$ref: "#/components/responses/ServiceUnavailableError"
/api/v1/internal/deliveries/{delivery_id}/attempts:
get:
tags:
- OperatorIntegration
operationId: listDeliveryAttempts
summary: Get the attempt history of one durable delivery
parameters:
- $ref: "#/components/parameters/DeliveryIDPath"
responses:
"200":
description: The ordered attempt history of one durable delivery.
content:
application/json:
schema:
$ref: "#/components/schemas/DeliveryAttemptsResponse"
"400":
$ref: "#/components/responses/InvalidRequestError"
"404":
$ref: "#/components/responses/DeliveryNotFoundError"
"500":
$ref: "#/components/responses/InternalError"
"503":
$ref: "#/components/responses/ServiceUnavailableError"
/api/v1/internal/deliveries/{delivery_id}/resend:
post:
tags:
- OperatorIntegration
operationId: resendDelivery
summary: Clone one terminal delivery for resend
parameters:
- $ref: "#/components/parameters/DeliveryIDPath"
responses:
"200":
description: The clone delivery was created successfully.
content:
application/json:
schema:
$ref: "#/components/schemas/DeliveryResendResponse"
"400":
$ref: "#/components/responses/InvalidRequestError"
"404":
$ref: "#/components/responses/DeliveryNotFoundError"
"409":
$ref: "#/components/responses/ResendNotAllowedError"
"500":
$ref: "#/components/responses/InternalError"
"503":
$ref: "#/components/responses/ServiceUnavailableError"
components:
parameters:
IdempotencyKey:
name: Idempotency-Key
in: header
required: true
description: |
Caller-owned stable deduplication key. `Auth / Session Service` uses
the created `challenge_id` as the raw header value.
schema:
type: string
DeliveryIDPath:
name: delivery_id
in: path
required: true
description: Mail Service delivery identifier.
schema:
type: string
RecipientFilter:
name: recipient
in: query
required: false
description: Effective-recipient filter covering `to`, `cc`, and `bcc`.
schema:
type: string
StatusFilter:
name: status
in: query
required: false
description: Delivery lifecycle status filter.
schema:
type: string
enum:
- queued
- rendered
- sending
- sent
- suppressed
- failed
- dead_letter
SourceFilter:
name: source
in: query
required: false
description: Delivery source filter.
schema:
type: string
enum:
- authsession
- notification
- operator_resend
TemplateIDFilter:
name: template_id
in: query
required: false
description: Template family filter.
schema:
type: string
IdempotencyKeyFilter:
name: idempotency_key
in: query
required: false
description: |
Idempotency-key filter. When `source` is omitted, Mail Service matches
the key across all frozen sources.
schema:
type: string
FromCreatedAtMSFilter:
name: from_created_at_ms
in: query
required: false
description: Inclusive lower bound for `created_at_ms`.
schema:
type: integer
format: int64
ToCreatedAtMSFilter:
name: to_created_at_ms
in: query
required: false
description: Inclusive upper bound for `created_at_ms`.
schema:
type: integer
format: int64
ListLimit:
name: limit
in: query
required: false
description: |
Maximum number of returned deliveries. The frozen default is `50` and
the maximum is `200`.
schema:
type: integer
minimum: 1
maximum: 200
ListCursor:
name: cursor
in: query
required: false
description: |
Opaque continuation cursor encoded as base64url of
`created_at_ms:delivery_id`.
schema:
type: string
schemas:
LoginCodeDeliveryRequest:
type: object
additionalProperties: false
required:
- email
- code
- locale
properties:
email:
type: string
description: Normalized destination e-mail address.
code:
type: string
description: Exact login code generated by `Auth / Session Service`.
locale:
type: string
description: Canonical BCP 47 language tag already resolved upstream.
LoginCodeDeliveryResponse:
type: object
additionalProperties: false
required:
- outcome
properties:
outcome:
type: string
description: Stable coarse outcome of the auth-delivery acceptance.
enum:
- sent
- suppressed
DeliverySummaryResponse:
type: object
additionalProperties: false
required:
- delivery_id
- source
- payload_mode
- to
- cc
- bcc
- reply_to
- locale_fallback_used
- idempotency_key
- status
- attempt_count
- created_at_ms
- updated_at_ms
properties:
delivery_id:
type: string
resend_parent_delivery_id:
type: string
source:
type: string
enum:
- authsession
- notification
- operator_resend
payload_mode:
type: string
enum:
- rendered
- template
template_id:
type: string
to:
type: array
items:
type: string
cc:
type: array
items:
type: string
bcc:
type: array
items:
type: string
reply_to:
type: array
items:
type: string
locale:
type: string
locale_fallback_used:
type: boolean
idempotency_key:
type: string
status:
type: string
enum:
- queued
- rendered
- sending
- sent
- suppressed
- failed
- dead_letter
attempt_count:
type: integer
last_attempt_status:
type: string
enum:
- scheduled
- in_progress
- render_failed
- provider_accepted
- provider_rejected
- transport_failed
- timed_out
provider_summary:
type: string
created_at_ms:
type: integer
format: int64
updated_at_ms:
type: integer
format: int64
sent_at_ms:
type: integer
format: int64
suppressed_at_ms:
type: integer
format: int64
failed_at_ms:
type: integer
format: int64
dead_lettered_at_ms:
type: integer
format: int64
DeliveryListResponse:
type: object
additionalProperties: false
required:
- items
properties:
items:
type: array
items:
$ref: "#/components/schemas/DeliverySummaryResponse"
next_cursor:
type: string
AttachmentResponse:
type: object
additionalProperties: false
required:
- filename
- content_type
- size_bytes
properties:
filename:
type: string
content_type:
type: string
size_bytes:
type: integer
format: int64
DeadLetterResponse:
type: object
additionalProperties: false
required:
- delivery_id
- final_attempt_no
- failure_classification
- created_at_ms
properties:
delivery_id:
type: string
final_attempt_no:
type: integer
failure_classification:
type: string
provider_summary:
type: string
created_at_ms:
type: integer
format: int64
recovery_hint:
type: string
DeliveryDetailResponse:
type: object
additionalProperties: false
required:
- delivery_id
- source
- payload_mode
- to
- cc
- bcc
- reply_to
- attachments
- locale_fallback_used
- idempotency_key
- status
- attempt_count
- created_at_ms
- updated_at_ms
properties:
delivery_id:
type: string
resend_parent_delivery_id:
type: string
source:
type: string
enum:
- authsession
- notification
- operator_resend
payload_mode:
type: string
enum:
- rendered
- template
template_id:
type: string
template_variables:
type: object
additionalProperties: true
to:
type: array
items:
type: string
cc:
type: array
items:
type: string
bcc:
type: array
items:
type: string
reply_to:
type: array
items:
type: string
subject:
type: string
text_body:
type: string
html_body:
type: string
attachments:
type: array
items:
$ref: "#/components/schemas/AttachmentResponse"
locale:
type: string
locale_fallback_used:
type: boolean
idempotency_key:
type: string
status:
type: string
enum:
- queued
- rendered
- sending
- sent
- suppressed
- failed
- dead_letter
attempt_count:
type: integer
last_attempt_status:
type: string
enum:
- scheduled
- in_progress
- render_failed
- provider_accepted
- provider_rejected
- transport_failed
- timed_out
provider_summary:
type: string
created_at_ms:
type: integer
format: int64
updated_at_ms:
type: integer
format: int64
sent_at_ms:
type: integer
format: int64
suppressed_at_ms:
type: integer
format: int64
failed_at_ms:
type: integer
format: int64
dead_lettered_at_ms:
type: integer
format: int64
dead_letter:
$ref: "#/components/schemas/DeadLetterResponse"
AttemptResponse:
type: object
additionalProperties: false
required:
- delivery_id
- attempt_no
- scheduled_for_ms
- status
properties:
delivery_id:
type: string
attempt_no:
type: integer
scheduled_for_ms:
type: integer
format: int64
started_at_ms:
type: integer
format: int64
finished_at_ms:
type: integer
format: int64
status:
type: string
enum:
- scheduled
- in_progress
- render_failed
- provider_accepted
- provider_rejected
- transport_failed
- timed_out
provider_classification:
type: string
provider_summary:
type: string
DeliveryAttemptsResponse:
type: object
additionalProperties: false
required:
- items
properties:
items:
type: array
items:
$ref: "#/components/schemas/AttemptResponse"
DeliveryResendResponse:
type: object
additionalProperties: false
required:
- delivery_id
properties:
delivery_id:
type: string
ErrorResponse:
type: object
additionalProperties: false
required:
- error
properties:
error:
$ref: "#/components/schemas/ErrorBody"
ErrorBody:
type: object
additionalProperties: false
required:
- code
- message
properties:
code:
type: string
description: Stable internal API error code.
message:
type: string
description: Human-readable trusted error message.
responses:
InvalidRequestError:
description: Request validation failed.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
missingHeader:
value:
error:
code: invalid_request
message: Idempotency-Key header must not be empty
invalidCursor:
value:
error:
code: invalid_request
message: cursor is invalid
ConflictError:
description: The current idempotency scope belongs to a different normalized request.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
conflict:
value:
error:
code: conflict
message: request conflicts with current state
DeliveryNotFoundError:
description: The requested delivery does not exist.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
missingDelivery:
value:
error:
code: delivery_not_found
message: delivery not found
ResendNotAllowedError:
description: The requested delivery is not in a terminal resend-eligible state.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
resendNotAllowed:
value:
error:
code: resend_not_allowed
message: delivery status does not allow resend
InternalError:
description: Internal application invariant failed.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
internal:
value:
error:
code: internal_error
message: internal server error
ServiceUnavailableError:
description: Durable acceptance or trusted delivery inspection could not be completed.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
unavailable:
value:
error:
code: service_unavailable
message: service is unavailable