openapi: 3.1.0 info: title: HAN SMS Service version: 1.0.0 description: Durable internal SMS ordering and i-Digital delivery callbacks. servers: - url: http://sms-service:8080 paths: /internal/sms/v1/send: post: operationId: orderSms security: - serviceBearer: [] parameters: - $ref: "#/components/parameters/RequestId" - $ref: "#/components/parameters/Traceparent" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/SendRequest" responses: "202": description: New order durably committed; provider has not necessarily been called. content: application/json: schema: $ref: "#/components/schemas/SendResponse" "200": description: Idempotent replay of an existing order. content: application/json: schema: $ref: "#/components/schemas/SendResponse" "401": $ref: "#/components/responses/Unauthorized" "409": $ref: "#/components/responses/IdempotencyConflict" "422": $ref: "#/components/responses/InvalidRequest" "429": $ref: "#/components/responses/RateLimited" "503": $ref: "#/components/responses/Unavailable" /internal/sms/v1/messages/{sms_message_id}: get: operationId: readSmsOrder security: - serviceBearer: [] parameters: - name: sms_message_id in: path required: true schema: type: string format: uuid - $ref: "#/components/parameters/RequestId" responses: "200": description: Redacted message diagnostics; never contains OTP, body, or full phone. content: application/json: schema: $ref: "#/components/schemas/Message" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" /callbacks/idgtl/sms: post: operationId: acceptIdgtlCallback security: - callbackBasic: [] requestBody: required: true content: application/json: schema: type: array minItems: 1 maxItems: 1000 items: $ref: "#/components/schemas/IdgtlCallbackItem" responses: "204": description: Valid callback items committed; invalid items were safely ignored. "401": $ref: "#/components/responses/Unauthorized" "422": $ref: "#/components/responses/InvalidRequest" webhooks: idgtlDeliveryStatus: post: summary: The same payload accepted at /callbacks/idgtl/sms. security: - callbackBasic: [] requestBody: required: true content: application/json: schema: type: array items: $ref: "#/components/schemas/IdgtlCallbackItem" responses: "204": description: Callback committed. components: securitySchemes: serviceBearer: type: http scheme: bearer bearerFormat: opaque-service-token callbackBasic: type: http scheme: basic parameters: RequestId: name: X-Request-ID in: header required: false schema: type: string maxLength: 128 Traceparent: name: traceparent in: header required: false schema: type: string pattern: "^[\\da-f]{2}-[\\da-f]{32}-[\\da-f]{16}-[\\da-f]{2}$" schemas: SendRequest: type: object additionalProperties: false required: - idempotency_key - template_code - locale - phone_e164 - substitutions - customer_ref - message_ttl_sec properties: idempotency_key: type: string minLength: 8 maxLength: 192 template_code: const: auth_otp locale: const: ru phone_e164: type: string pattern: "^\\+[1-9]\\d{7,14}$" substitutions: type: object additionalProperties: false required: [code, ttl_min] properties: code: type: string pattern: "^\\d{4,10}$" ttl_min: oneOf: - type: string pattern: "^\\d{1,3}$" - type: integer minimum: 1 maximum: 1440 customer_ref: type: string minLength: 1 maxLength: 128 message_ttl_sec: type: integer minimum: 60 maximum: 86400 SendResponse: type: object additionalProperties: false required: [sms_message_id, ordered_at] properties: sms_message_id: type: string format: uuid ordered_at: type: string format: date-time Message: type: object additionalProperties: false description: Deliberately excludes phone_e164, body_rendered, and substitutions. required: - sms_message_id - ordered_at - updated_at - requester_service - process - channel - provider - phone_masked - template_code - send_status - delivery_status - attempt_count properties: sms_message_id: {type: string, format: uuid} ordered_at: {type: string, format: date-time} updated_at: {type: string, format: date-time} requester_service: {const: keycloak} process: {const: auth_otp} channel: {const: SMS} provider: {const: idgtl} phone_masked: {type: string} template_code: {const: auth_otp} customer_ref: {type: [string, "null"]} send_status: enum: [pending, accepted, rejected, failed, uncertain, skipped] delivery_status: enum: [unknown, sent, delivered, undelivered, unsent] provider_message_id: {type: [string, "null"]} accepted_at: {type: [string, "null"], format: date-time} sent_at: {type: [string, "null"], format: date-time} delivered_at: {type: [string, "null"], format: date-time} attempt_count: {type: integer, minimum: 0} provider_error_code: {type: [string, "null"]} IdgtlCallbackItem: type: object required: - channelType - messageUuid - externalMessageId - callbackEvent - status - statusTime properties: channelType: const: SMS messageUuid: type: string externalMessageId: type: string callbackEvent: type: string status: enum: [sent, delivered, undelivered, unsent] statusTime: type: string format: date-time errorCode: type: [string, "null"] parts: type: [integer, "null"] minimum: 0 price: type: [number, "null"] minimum: 0 currency: type: [string, "null"] minLength: 3 maxLength: 3 Error: type: object additionalProperties: false required: [error] properties: error: type: object additionalProperties: false required: [code, message, request_id, details] properties: code: {type: string} message: {type: string} request_id: {type: string} details: oneOf: - type: object - type: array responses: Unauthorized: description: Missing or invalid credentials. content: application/json: schema: {$ref: "#/components/schemas/Error"} IdempotencyConflict: description: The key was already used with another meaningful payload. content: application/json: schema: {$ref: "#/components/schemas/Error"} InvalidRequest: description: Strict request or callback validation failed. content: application/json: schema: {$ref: "#/components/schemas/Error"} RateLimited: description: Caller and destination rate limit exceeded. headers: Retry-After: schema: {type: integer} content: application/json: schema: {$ref: "#/components/schemas/Error"} Unavailable: description: The order could not be durably committed. content: application/json: schema: {$ref: "#/components/schemas/Error"} NotFound: description: Message was not found in the caller scope. content: application/json: schema: {$ref: "#/components/schemas/Error"}