openapi: 3.1.0 info: title: Payment Platform Partner API description: Versioned server-to-server contract for partner integrations. Responses use a JSON envelope with data and meta.request_id; errors use error and meta.request_id. Partner credentials are limited to 120 requests per minute by default; HTTP 429 includes Retry-After. Payment acceptance is asynchronous and does not prove settlement. Webhook endpoint and delivery operations are included. Only partner routes are included. license: name: '' version: 1.3.0 servers: - url: "https://api-pay.edutecka.com" paths: /v1/customers: get: tags: - Customers summary: List customers description: Returns up to 100 customers belonging to the authenticated tenant. operationId: listCustomersV1 responses: '200': description: Customer list content: application/json: schema: $ref: '#/components/schemas/CustomerListResponse' '401': description: Missing, invalid, expired or revoked API key content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: API key lacks the required scope content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] post: tags: - Customers summary: Create a customer description: Creates a customer in the authenticated tenant and environment. Replays with the same idempotency key return the same resource ID and its current representation. operationId: createCustomerV1 parameters: - name: Idempotency-Key in: header description: Unique request key; replaying the same key and body does not create another customer. required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/NewCustomer' required: true responses: '201': description: Customer created content: application/json: schema: $ref: '#/components/schemas/CustomerResponse' '400': description: Invalid input content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '401': description: Missing, invalid, expired or revoked API key content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: API key lacks the required scope content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '409': description: Idempotency key conflicts with another request content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] /v1/customers/{id}: get: tags: - Customers summary: Retrieve a customer description: Retrieves a customer in the authenticated tenant and API-key environment. operationId: getCustomerV1 parameters: - name: id in: path description: Customer ID required: true schema: type: string responses: '200': description: Customer content: application/json: schema: $ref: '#/components/schemas/CustomerResponse' '401': description: Missing, invalid, expired or revoked API key content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: API key lacks the required scope content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '404': description: Customer not found content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] patch: tags: - Customers summary: Update a customer description: Updates the supplied customer fields in the authenticated tenant and API-key environment. Set email to an empty string to clear it. Requires an Idempotency-Key. operationId: updateCustomerV1 parameters: - name: id in: path description: Customer ID required: true schema: type: string - name: Idempotency-Key in: header description: Stable key for safe retries; same key with a different request returns 409. required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/UpdateCustomer' required: true responses: '200': description: Customer updated content: application/json: schema: $ref: '#/components/schemas/CustomerResponse' '400': description: Invalid input or missing idempotency key content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '401': description: Missing, invalid, expired or revoked API key content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: API key lacks the required scope content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '404': description: Customer not found content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '409': description: Idempotency key conflicts with another request content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] /v1/fx/quote: get: tags: - FX summary: Get an indicative MZN/USD/EUR conversion quote description: Returns a read-only quote from the active configured rate set. Foreign-to-MZN uses the buy rate; MZN-to-foreign uses the sell rate. The quote does not move funds and expires with the rate set. operationId: getFxQuoteV1 parameters: - name: from_currency in: query required: true schema: type: string example: USD - name: to_currency in: query required: true schema: type: string example: MZN - name: amount_minor in: query required: true schema: type: integer format: int64 minimum: 1 example: 10000 responses: '200': description: Indicative conversion quote '403': description: Credential lacks fx:read '422': description: Invalid amount, currency or pair '503': description: No active rate is configured or database is unavailable /v1/payment-links: get: tags: - Payment Links summary: List payment links description: Returns up to 100 links belonging to the authenticated tenant. operationId: listPaymentLinksV1 responses: '200': description: Payment link list content: application/json: schema: $ref: '#/components/schemas/PaymentLinkListResponse' '401': description: Missing, invalid, expired or revoked API key content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: API key lacks the required scope content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] post: tags: - Payment Links summary: Create a payment link description: Creates a fixed-amount link in the account base currency (MZN, USD or EUR). Amounts use minor units (100 = 1.00 unit). The platform generates the ID and reference. This operation does not create a hosted checkout or initiate a payment. operationId: createPaymentLinkV1 parameters: - name: Idempotency-Key in: header description: Unique request key; replaying the same key and body returns the same resource ID. required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/NewLink' required: true responses: '201': description: Link created; reference generated by the platform content: application/json: schema: $ref: '#/components/schemas/PaymentLinkResponse' '400': description: Invalid input content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '401': description: Missing, invalid, expired or revoked API key content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: API key lacks the required scope content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '409': description: Idempotency key conflicts with another request content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '422': description: Currency is unsupported or expiration is invalid or in the past content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] /v1/payment-links/{id}: get: tags: - Payment Links summary: Retrieve a payment link description: Returns a link only when it belongs to the authenticated tenant. operationId: getPaymentLinkV1 parameters: - name: id in: path description: Platform-generated payment link ID required: true schema: type: string responses: '200': description: Payment link content: application/json: schema: $ref: '#/components/schemas/PaymentLinkResponse' '401': description: Missing, invalid, expired or revoked API key content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: API key lacks the required scope content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '404': description: Resource not found for this tenant content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] /v1/payments: get: tags: - Payments summary: List payment intents operationId: listPaymentsV1 parameters: - name: limit in: query required: false schema: type: integer format: int64 maximum: 100 minimum: 1 responses: '200': description: Payments in the credential environment content: application/json: schema: $ref: '#/components/schemas/PaymentListResponse' post: tags: - Payments summary: Create a payment intent description: Creates an idempotent payment intent in the API-key environment and queues one private Master request. 202 means accepted for processing, not paid. A pending or unknown result is not a settlement. operationId: createPaymentV1 parameters: - name: Idempotency-Key in: header description: Required stable key, 16–128 safe characters. required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/CreatePaymentRequest' required: true responses: '202': description: Intent persisted for processing content: application/json: schema: $ref: '#/components/schemas/PaymentResponse' '400': description: Invalid request '403': description: Credential scope/environment is not allowed or production payments are disabled '409': description: Idempotency or account payment policy conflict '422': description: Invalid amount, phone, method, or currency '503': description: Master or encryption configuration is unavailable /v1/payments/{id}: get: tags: - Payments summary: Retrieve a payment intent operationId: getPaymentV1 parameters: - name: id in: path description: Platform-generated payment ID required: true schema: type: string responses: '200': description: Payment intent content: application/json: schema: $ref: '#/components/schemas/PaymentResponse' '404': description: Payment not found in this environment /v1/payments/{id}/attempts: get: tags: - Payments summary: List payment dispatch attempts operationId: listPaymentAttemptsV1 parameters: - name: id in: path description: Platform-generated payment ID required: true schema: type: string responses: '200': description: Safe attempt summaries for the payment content: application/json: schema: $ref: '#/components/schemas/PaymentAttemptListResponse' '404': description: Payment not found in this environment /v1/payments/{id}/events: get: tags: - Payments summary: List payment events operationId: listPaymentEventsV1 parameters: - name: id in: path description: Platform-generated payment ID required: true schema: type: string responses: '200': description: Sanitized payment timeline content: application/json: schema: $ref: '#/components/schemas/PaymentEventListResponse' '404': description: Payment not found in this environment /v1/webhook-deliveries: get: tags: - Webhooks summary: List webhook deliveries description: Lists delivery status and the last response metadata for the authenticated key environment. Response bodies and endpoint secrets are never stored or returned. operationId: listWebhookDeliveriesV1 parameters: - name: limit in: query required: false schema: type: integer format: int64 maximum: 100 minimum: 1 responses: '200': description: Webhook delivery list content: application/json: schema: $ref: '#/components/schemas/WebhookDeliveryListResponse' '403': description: Missing scope or environment content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] /v1/webhook-deliveries/{id}: get: tags: - Webhooks summary: Retrieve a webhook delivery description: Returns the event type and safe attempt metadata, without response bodies, endpoint secrets or raw payloads. operationId: getWebhookDeliveryV1 parameters: - name: id in: path description: Platform-generated delivery ID required: true schema: type: string responses: '200': description: Webhook delivery detail content: application/json: schema: $ref: '#/components/schemas/WebhookDeliveryResponse' '403': description: Missing scope or environment content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '404': description: Delivery not found in this environment content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] /v1/webhook-deliveries/{id}/retry: post: tags: - Webhooks summary: Retry a webhook delivery description: Schedules a failed delivery again if its endpoint is active. Repeated calls while the same delivery is pending or delivered do not create a second delivery. operationId: retryWebhookDeliveryV1 parameters: - name: id in: path description: Platform-generated delivery ID required: true schema: type: string responses: '200': description: Delivery was already completed content: application/json: schema: $ref: '#/components/schemas/WebhookDeliverySummaryResponse' '202': description: Retry scheduled or already pending content: application/json: schema: $ref: '#/components/schemas/WebhookDeliverySummaryResponse' '403': description: Missing scope or environment content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '404': description: Delivery not found content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '409': description: Delivery cannot be retried while its endpoint is disabled content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] /v1/webhook-endpoints: get: tags: - Webhooks summary: List webhook endpoints description: Lists endpoints in the authenticated API key environment. Signing secrets are never returned. operationId: listWebhookEndpointsV1 responses: '200': description: Endpoint list content: application/json: schema: $ref: '#/components/schemas/WebhookEndpointListResponse' '403': description: Missing scope or environment content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] post: tags: - Webhooks summary: Register a partner webhook endpoint description: Registers an HTTPS URL and event subscriptions. The signing secret is returned only once. Replaying an Idempotency-Key with the same request returns the endpoint without revealing the secret again. operationId: createWebhookEndpointV1 parameters: - name: Idempotency-Key in: header description: Stable key required to make endpoint creation idempotent. required: true schema: type: string requestBody: content: application/json: schema: $ref: '#/components/schemas/NewWebhookEndpoint' required: true responses: '200': description: Idempotent replay; secret is not returned content: application/json: schema: $ref: '#/components/schemas/WebhookEndpointResponse' '201': description: Endpoint created; secret included once content: application/json: schema: $ref: '#/components/schemas/WebhookEndpointResponse' '400': description: Invalid URL, event list or idempotency key content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '403': description: Missing scope or environment-bound API key content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '409': description: Idempotency key was reused with a different request content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '503': description: Webhook encryption is not configured content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] /v1/webhook-endpoints/{id}/disable: post: tags: - Webhooks summary: Disable a webhook endpoint description: Disables future deliveries for an endpoint in the authenticated key environment. Already queued attempts are not retried after the endpoint is disabled. operationId: disableWebhookEndpointV1 parameters: - name: id in: path description: Platform-generated webhook endpoint ID required: true schema: type: string responses: '200': description: Endpoint disabled content: application/json: schema: $ref: '#/components/schemas/WebhookEndpointSummaryResponse' '403': description: Missing scope or environment content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '404': description: Endpoint not found content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '500': description: Internal service error content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' security: - BearerAuth: [] components: schemas: CreatePaymentRequest: type: object required: - amount_minor - currency - method - phone properties: amount_minor: type: integer format: int64 example: 10000 minimum: 1 currency: type: string description: Must match the account base currency. eMola and M-Pesa are charged in MZN. example: USD pattern: ^(MZN|USD|EUR)$ customer_id: type: - string - 'null' description: type: - string - 'null' method: type: string example: emola phone: type: string example: '861234567' reference: type: - string - 'null' additionalProperties: false Customer: type: object required: - id - name - created_at properties: created_at: type: string email: type: - string - 'null' id: type: string name: type: string CustomerListResponse: type: object required: - data - meta properties: data: type: array items: $ref: '#/components/schemas/Customer' meta: $ref: '#/components/schemas/ResponseMeta' CustomerResponse: type: object required: - data - meta properties: data: $ref: '#/components/schemas/Customer' meta: $ref: '#/components/schemas/ResponseMeta' ErrorEnvelope: type: object required: - error - meta properties: error: $ref: '#/components/schemas/ErrorInfo' meta: $ref: '#/components/schemas/ResponseMeta' ErrorInfo: type: object required: - type - code - message - details properties: code: type: string details: {} message: type: string type: type: string FxQuoteQuery: type: object required: - from_currency - to_currency - amount_minor properties: amount_minor: type: integer format: int64 example: 10000 minimum: 1 from_currency: type: string example: USD pattern: ^(MZN|USD|EUR)$ to_currency: type: string example: MZN pattern: ^(MZN|USD|EUR)$ additionalProperties: false NewCustomer: type: object required: - name properties: email: type: - string - 'null' maxLength: 320 name: type: string maxLength: 200 minLength: 1 additionalProperties: false NewLink: type: object required: - amount_minor - currency properties: amount_minor: type: integer format: int64 minimum: 1 currency: type: string example: MZN pattern: ^(MZN|USD|EUR)$ expires_at: type: - string - 'null' format: date-time additionalProperties: false NewWebhookEndpoint: type: object required: - url - events properties: events: type: array items: type: string example: - payment.succeeded - payment.failed url: type: string example: https://merchant.example.com/payment-events additionalProperties: false PaymentAttemptListResponse: type: object required: - data - meta properties: data: type: array items: $ref: '#/components/schemas/PaymentAttemptView' meta: $ref: '#/components/schemas/PaymentMeta' PaymentAttemptView: type: object required: - id - attempt_no - method - status - requested_at - updated_at properties: attempt_no: type: integer format: int32 completed_at: type: - string - 'null' id: type: string method: type: string requested_at: type: string status: type: string updated_at: type: string PaymentEventListResponse: type: object required: - data - meta properties: data: type: array items: $ref: '#/components/schemas/PaymentEventView' meta: $ref: '#/components/schemas/PaymentMeta' PaymentEventView: type: object required: - id - type - to_status - details - occurred_at properties: details: {} from_status: type: - string - 'null' id: type: string occurred_at: type: string to_status: type: string type: type: string PaymentLink: type: object required: - id - reference - currency - status - created_at properties: amount_minor: type: - integer - 'null' format: int64 created_at: type: string currency: type: string expires_at: type: - string - 'null' id: type: string reference: type: string status: type: string PaymentLinkListResponse: type: object required: - data - meta properties: data: type: array items: $ref: '#/components/schemas/PaymentLink' meta: $ref: '#/components/schemas/ResponseMeta' PaymentLinkResponse: type: object required: - data - meta properties: data: $ref: '#/components/schemas/PaymentLink' meta: $ref: '#/components/schemas/ResponseMeta' PaymentListResponse: type: object required: - data - meta properties: data: type: array items: $ref: '#/components/schemas/PaymentView' meta: $ref: '#/components/schemas/PaymentMeta' PaymentMeta: type: object required: - request_id properties: request_id: type: string PaymentResponse: type: object required: - data - meta properties: data: $ref: '#/components/schemas/PaymentView' meta: $ref: '#/components/schemas/PaymentMeta' PaymentView: type: object required: - id - reference - trans_id - amount_minor - currency - base_amount_minor - base_currency - base_platform_fee_minor - base_receiver_net_minor - method - status - platform_fee_bps - platform_fee_minor - receiver_net_minor - created_at - updated_at properties: amount_minor: type: integer format: int64 base_amount_minor: type: integer format: int64 base_currency: type: string base_platform_fee_minor: type: integer format: int64 base_receiver_net_minor: type: integer format: int64 created_at: type: string currency: type: string fx_effective_at: type: - string - 'null' fx_expires_at: type: - string - 'null' fx_rate_mzn_per_unit: type: - string - 'null' fx_rate_set_id: type: - string - 'null' fx_rate_side: type: - string - 'null' fx_rate_source: type: - string - 'null' id: type: string method: type: string platform_fee_bps: type: integer format: int32 platform_fee_minor: type: integer format: int64 receiver_net_minor: type: integer format: int64 reference: type: string status: type: string trans_id: type: string updated_at: type: string ResponseMeta: type: object required: - request_id properties: request_id: type: string UpdateCustomer: type: object properties: email: type: - string - 'null' maxLength: 320 name: type: - string - 'null' maxLength: 200 minLength: 1 additionalProperties: false WebhookDeliveryAttemptView: type: object required: - attempt_no - outcome - duration_ms - created_at properties: attempt_no: type: integer format: int32 created_at: type: string duration_ms: type: integer format: int32 error_code: type: - string - 'null' http_status: type: - integer - 'null' format: int32 outcome: type: string WebhookDeliveryDetailView: type: object required: - delivery - attempts properties: attempts: type: array items: $ref: '#/components/schemas/WebhookDeliveryAttemptView' delivery: $ref: '#/components/schemas/WebhookDeliveryView' WebhookDeliveryListResponse: type: object required: - data - meta properties: data: type: array items: $ref: '#/components/schemas/WebhookDeliveryView' meta: $ref: '#/components/schemas/ResponseMeta' WebhookDeliveryResponse: type: object required: - data - meta properties: data: $ref: '#/components/schemas/WebhookDeliveryDetailView' meta: $ref: '#/components/schemas/ResponseMeta' WebhookDeliverySummaryResponse: type: object required: - data - meta properties: data: $ref: '#/components/schemas/WebhookDeliveryView' meta: $ref: '#/components/schemas/ResponseMeta' WebhookDeliveryView: type: object required: - id - event_id - event_type - status - attempt_count - created_at properties: attempt_count: type: integer format: int32 created_at: type: string delivered_at: type: - string - 'null' event_id: type: string event_type: type: string id: type: string last_error_code: type: - string - 'null' last_http_status: type: - integer - 'null' format: int32 status: type: string WebhookEndpointListResponse: type: object required: - data - meta properties: data: type: array items: $ref: '#/components/schemas/WebhookEndpointSummaryView' meta: $ref: '#/components/schemas/ResponseMeta' WebhookEndpointResponse: type: object required: - data - meta properties: data: $ref: '#/components/schemas/WebhookEndpointView' meta: $ref: '#/components/schemas/ResponseMeta' WebhookEndpointSummaryResponse: type: object required: - data - meta properties: data: $ref: '#/components/schemas/WebhookEndpointSummaryView' meta: $ref: '#/components/schemas/ResponseMeta' WebhookEndpointSummaryView: type: object required: - id - url - events - status - created_at - updated_at properties: created_at: type: string events: type: array items: type: string id: type: string status: type: string updated_at: type: string url: type: string WebhookEndpointView: type: object required: - id - url - events - status - created_at - updated_at properties: created_at: type: string events: type: array items: type: string id: type: string secret: type: - string - 'null' status: type: string updated_at: type: string url: type: string securitySchemes: BearerAuth: type: http scheme: bearer description: 'Partner API key sent as Authorization: Bearer ' security: - BearerAuth: [] tags: - name: Customers description: Tenant-scoped customer records. - name: Payment Links description: Create and inspect fixed-amount payment links. - name: Payments description: Asynchronous, environment-scoped payment intents and event history. - name: FX description: Indicative MZN, USD and EUR quotes; no funds are moved. - name: Webhooks description: Register HTTPS endpoints for asynchronous payment events.