315 lines
8.8 KiB
YAML
315 lines
8.8 KiB
YAML
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"}
|