{
  "openapi": "3.1.0",
  "info": {
    "title": "Quad State SMS API",
    "version": "2026-09-30",
    "description": "Customer-scoped messaging, per-number channel capabilities, message history, and signed webhooks. SMS is implemented. MMS, RCS, and iMessage are reserved channels and report unavailable until implemented and provisioned.",
    "contact": {
      "name": "Quad State"
    }
  },
  "servers": [
    {
      "url": "https://api.quadstate.us"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/account": {
      "get": {
        "operationId": "getAccount",
        "summary": "Get your customer account",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name"
                  ]
                }
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/numbers": {
      "get": {
        "operationId": "listNumbers",
        "summary": "List assigned numbers and channel capabilities",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Number"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/messages": {
      "post": {
        "operationId": "sendMessage",
        "summary": "Submit a message with a channel and idempotency key",
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                },
                "description": "Whether this returns a previous request."
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Idempotent replay; no new SMS sent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            },
            "headers": {
              "Idempotency-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true",
                    "false"
                  ]
                },
                "description": "Whether this returns a previous request."
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "from": {
                    "type": "string"
                  },
                  "to": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 1600
                  },
                  "channel": {
                    "type": "string",
                    "enum": [
                      "sms",
                      "mms",
                      "rcs",
                      "imessage"
                    ],
                    "default": "sms",
                    "description": "SMS is implemented now. Other channels are reserved and return channel_unavailable until activated."
                  }
                },
                "required": [
                  "from",
                  "to",
                  "text"
                ]
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._:-]{1,128}$"
            }
          }
        ],
        "description": "202 acknowledges a stored submission result, not handset delivery. Inspect status: accepted, failed, submitting, or unknown. Never use a new key to retry an uncertain result. channel defaults to sms for existing clients. Check your number capabilities before submitting. Unavailable channels return 422 channel_unavailable and are never downgraded to SMS. Pending sending activation returns 503 sending_unavailable."
      },
      "get": {
        "operationId": "listMessages",
        "summary": "List your messages",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Message"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "next_cursor"
                  ]
                }
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/messages/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getMessage",
        "summary": "Get a message from your account",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhook": {
      "get": {
        "operationId": "getWebhook",
        "summary": "Get webhook settings",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "configured": {
                      "type": "boolean"
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "url",
                    "configured",
                    "events"
                  ]
                }
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "setWebhook",
        "summary": "Set a public HTTPS receiving URL, or null to pause deliveries",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "configured": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "url",
                    "configured"
                  ]
                }
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri"
                  }
                },
                "required": [
                  "url"
                ]
              }
            }
          }
        }
      }
    },
    "/webhook/rotate-secret": {
      "post": {
        "operationId": "rotateWebhookSecret",
        "summary": "Immediately rotate the webhook signing secret",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "secret"
                  ]
                }
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhook/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "summary": "List the latest 100 webhook delivery records",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Delivery"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhook/deliveries/{id}/retry": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "operationId": "retryWebhookDelivery",
        "summary": "Queue a webhook replay with the same event ID",
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "const": "pending",
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "status"
                  ]
                }
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/health": {
      "get": {
        "operationId": "health",
        "summary": "Check service health",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "service": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "service",
                    "status"
                  ]
                }
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/numbers/{number}": {
      "parameters": [
        {
          "name": "number",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^\\+1[2-9]\\d{2}[2-9]\\d{6}$"
          },
          "example": "+12705551234"
        }
      ],
      "get": {
        "operationId": "getNumber",
        "summary": "Get an assigned number and its capabilities",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Number"
                }
              }
            }
          },
          "404": {
            "description": "Number not assigned to this customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/numbers/{number}/capabilities": {
      "parameters": [
        {
          "name": "number",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^\\+1[2-9]\\d{2}[2-9]\\d{6}$"
          },
          "example": "+12705551234"
        }
      ],
      "get": {
        "operationId": "getNumberCapabilities",
        "summary": "Get SMS, MMS, RCS, and iMessage availability",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "number": {
                      "type": "string"
                    },
                    "capabilities": {
                      "$ref": "#/components/schemas/NumberCapabilities"
                    }
                  },
                  "required": [
                    "number",
                    "capabilities"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Number not assigned to this customer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "default": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "smsEvent": {
      "post": {
        "summary": "Quad State SMS event delivery",
        "description": "Verify HMAC-SHA256 of timestamp + dot + raw UTF-8 body using your webhook secret; reject timestamps outside 300 seconds and deduplicate event IDs. At least once; ordering is not guaranteed. Up to 12 attempts with exponential backoff from 30s to 1h. Return 2xx after durable storage.",
        "parameters": [
          {
            "name": "X-Quad-State-Event-ID",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Quad-State-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "t=UNIX_TIMESTAMP,sha256=HMAC_SHA256_HEX"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Event"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event saved and acknowledged"
          }
        },
        "security": []
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Quad State customer Bearer token; new tokens contain 48 uppercase hexadecimal characters"
      }
    },
    "schemas": {
      "Message": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "const": "sms_message",
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "submitting",
              "accepted",
              "failed",
              "unknown",
              "received"
            ]
          },
          "error_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "channel": {
            "type": "string",
            "enum": [
              "sms",
              "mms",
              "rcs",
              "imessage"
            ],
            "default": "sms",
            "description": "SMS is implemented now. Other channels are reserved and return channel_unavailable until activated."
          }
        },
        "required": [
          "id",
          "object",
          "direction",
          "from",
          "to",
          "text",
          "status",
          "error_code",
          "created_at",
          "updated_at",
          "channel"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message",
              "request_id"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "sms.received",
              "sms.status_updated"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "$ref": "#/components/schemas/Message"
          }
        },
        "required": [
          "id",
          "type",
          "created_at",
          "data"
        ]
      },
      "Delivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "event_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "delivered",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "last_http_status": {
            "type": [
              "integer",
              "null"
            ]
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "event_id",
          "status",
          "attempts",
          "last_http_status",
          "last_error",
          "updated_at"
        ]
      },
      "CapabilityDirection": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "available",
              "unavailable"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "number_disabled",
              "not_supported",
              "not_enabled",
              "activation_pending",
              null
            ]
          }
        },
        "required": [
          "status",
          "reason"
        ]
      },
      "ChannelCapability": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "available",
              "unavailable"
            ]
          },
          "send": {
            "$ref": "#/components/schemas/CapabilityDirection"
          },
          "receive": {
            "$ref": "#/components/schemas/CapabilityDirection"
          }
        },
        "required": [
          "status",
          "send",
          "receive"
        ],
        "description": "status is available if either direction is available. Check send or receive for the operation you need."
      },
      "NumberCapabilities": {
        "type": "object",
        "properties": {
          "sms": {
            "$ref": "#/components/schemas/ChannelCapability"
          },
          "mms": {
            "$ref": "#/components/schemas/ChannelCapability"
          },
          "rcs": {
            "$ref": "#/components/schemas/ChannelCapability"
          },
          "imessage": {
            "$ref": "#/components/schemas/ChannelCapability"
          }
        },
        "required": [
          "sms",
          "mms",
          "rcs",
          "imessage"
        ],
        "description": "Reports implemented support and provisioning for this assigned number. Future channels remain unavailable until their adapters and number provisioning are enabled."
      },
      "Number": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "send_enabled": {
            "type": "boolean",
            "description": "Legacy SMS sending availability."
          },
          "capabilities": {
            "$ref": "#/components/schemas/NumberCapabilities"
          }
        },
        "required": [
          "number",
          "enabled",
          "send_enabled",
          "capabilities"
        ]
      }
    }
  }
}
