openapi: 3.1.0
info:
  title: MurtAPI
  version: 0.1.0
  description: |
    The safety layer between AI agents and commerce APIs.

    MurtAPI prevents duplicate refunds, broken auth, and rate-limit crashes
    when AI agents operate e-commerce stores. One API key. Every commerce API
    your agent needs — handled reliably.

    Supports: Shopify Admin, Shopify Storefront, Klaviyo, ShipStation, ShipBob,
    Gorgias, Recharge, Stripe, Postscript, Attentive.

    Free beta — sign up at https://murtapi.com
  contact:
    url: https://murtapi.com
  license:
    name: Proprietary

servers:
  - url: https://api.murtapi.com
    description: Production

security:
  - bearerAuth: []

paths:
  /health:
    get:
      summary: Health check
      description: Returns gateway status and version.
      operationId: getHealth
      security: []
      responses:
        '200':
          description: Gateway is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  version:
                    type: string
                    example: 0.1.0
                  phase:
                    type: integer
                    example: 1

  /proxy:
    post:
      summary: Proxy an API call
      description: |
        Send any API call through MurtAPI. The gateway handles:
        - Authentication (token retrieval + refresh)
        - Rate limiting (Shopify GraphQL cost budgeting + queuing)
        - Idempotency (webhook deduplication + mutation safety)
        - Error normalization (one error shape regardless of which API failed)
        - Response filtering (strips JSON bloat to agent-essential fields)

        Your agent makes one call. MurtAPI handles the rest.
      operationId: proxyRequest
      parameters:
        - in: header
          name: X-MurtAPI-User-Id
          required: true
          schema:
            type: string
          description: Your MurtAPI user ID
        - in: header
          name: X-Shop-Domain
          required: false
          schema:
            type: string
            example: my-store.myshopify.com
          description: Required for Shopify API calls
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProxyRequest'
            examples:
              shopify_graphql:
                summary: Shopify GraphQL query
                value:
                  api: shopify-admin
                  method: POST
                  path: /graphql.json
                  body:
                    query: "{ orders(first: 10) { edges { node { id name totalPrice } } } }"
              shopify_order_update:
                summary: Shopify order tag update (with idempotency)
                value:
                  api: shopify-admin
                  method: PUT
                  path: /orders/12345.json
                  body:
                    order:
                      id: 12345
                      tags: "vip,reviewed"
                  idempotencyKey: webhook_evt_abc123
              klaviyo_profile:
                summary: Klaviyo profile lookup
                value:
                  api: klaviyo
                  method: GET
                  path: /profiles/01GDDKASAP8TKDDA2GRZDSVP4H
      responses:
        '200':
          description: Successful proxy response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProxyResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MurtAPIError'
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MurtAPIError'
              example:
                code: AUTH_EXPIRED
                message: Shopify access token is invalid or expired.
                api: shopify-admin
                retryable: false
        '409':
          description: Duplicate action blocked or discount conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MurtAPIError'
              example:
                code: DUPLICATE_ACTION
                message: This webhook has already been processed.
                api: shopify-admin
                retryable: false
                idempotencyReplay: true
        '422':
          description: Mutation is not safe to retry
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MurtAPIError'
              example:
                code: MUTATION_UNSAFE
                message: "refundCreate on shopify-admin is not safe to retry — duplicate refunds likely."
                api: shopify-admin
                retryable: false
        '429':
          description: Rate limit hit — MurtAPI will queue and retry automatically
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MurtAPIError'
              example:
                code: RATE_LIMITED
                message: Shopify GraphQL rate limit hit. MurtAPI will queue and retry.
                api: shopify-admin
                retryable: true
                retryAfterMs: 1000
        '502':
          description: Downstream API error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MurtAPIError'

  /webhook/{api}:
    post:
      summary: Inbound webhook endpoint
      description: |
        Receive webhooks from any supported API. MurtAPI:
        1. Validates HMAC signature (Shopify, Klaviyo, Recharge, Stripe)
        2. Deduplicates delivery (documented duplicate behavior on all platforms)
        3. Enriches thin payloads (ShipStation resource_url auto-fetch)
        4. Forwards validated, enriched event to your agent endpoint
      operationId: receiveWebhook
      security: []
      parameters:
        - in: path
          name: api
          required: true
          schema:
            $ref: '#/components/schemas/ApiName'
      responses:
        '200':
          description: Webhook accepted
        '401':
          description: HMAC validation failed — webhook rejected

  /audit/credentials:
    get:
      summary: Credential health audit
      description: Lists the health status of all stored API credentials for your account.
      operationId: auditCredentials
      responses:
        '200':
          description: Credential audit results
          content:
            application/json:
              schema:
                type: object
                properties:
                  credentials:
                    type: array
                    items:
                      type: object
                      properties:
                        api:
                          $ref: '#/components/schemas/ApiName'
                        userId:
                          type: string
                        status:
                          type: string
                          enum: [ok, expiring, expired]

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Your MurtAPI key from the dashboard

  schemas:
    ApiName:
      type: string
      enum:
        - shopify-admin
        - shopify-storefront
        - klaviyo
        - shipstation
        - shipbob
        - gorgias
        - recharge
        - stripe
        - postscript
        - attentive
      description: The API to route the request to

    ProxyRequest:
      type: object
      required: [api, method, path]
      properties:
        api:
          $ref: '#/components/schemas/ApiName'
        method:
          type: string
          enum: [GET, POST, PUT, DELETE, PATCH]
        path:
          type: string
          description: API path relative to the base URL
          example: /graphql.json
        body:
          type: object
          description: Request body (for POST/PUT/PATCH)
        headers:
          type: object
          additionalProperties:
            type: string
          description: Additional headers to forward
        idempotencyKey:
          type: string
          description: |
            Unique key for idempotent operations. If provided:
            - Webhooks: deduplicated (same key = same event, processed once)
            - Mutations: safe retry (same key = same operation, result replayed)

    ProxyResponse:
      type: object
      properties:
        status:
          type: integer
        data:
          type: object
          description: Filtered API response (bloat removed)
        meta:
          type: object
          properties:
            api:
              $ref: '#/components/schemas/ApiName'
            cached:
              type: boolean
            rateLimitRemaining:
              type: integer
            idempotencyReplay:
              type: boolean
              description: True if this response was replayed from a prior idempotent request

    MurtAPIError:
      type: object
      description: |
        Normalized error shape. Every API failure returns this structure —
        agents handle one format regardless of which API failed.
      properties:
        code:
          type: string
          enum:
            - RATE_LIMITED
            - AUTH_EXPIRED
            - AUTH_INVALID
            - NOT_FOUND
            - DUPLICATE_ACTION
            - MUTATION_UNSAFE
            - VALIDATION_ERROR
            - DOWNSTREAM_ERROR
            - DISCOUNT_CONFLICT
            - ACTION_REQUIRED
            - UNKNOWN
        message:
          type: string
        api:
          $ref: '#/components/schemas/ApiName'
        retryable:
          type: boolean
          description: Whether the agent can safely retry this request
        retryAfterMs:
          type: integer
          description: If retryable, how long to wait before retrying
        raw:
          type: object
          description: Original error from the downstream API (for debugging)
