{
  "openapi": "3.1.0",
  "info": {
    "title": "DoNotify",
    "version": "1.0.0",
    "summary": "Place a real phone call to the user.",
    "description": "Makes the user's phone ring. The one channel that interrupts rather than queues, so it reaches people who are not looking at a screen, and it works on a basic handset or a landline with nothing installed. Use it when a notification is not enough: the user asked to be called, a deadline they will miss, a medication dose, or an escalation nobody has acknowledged. Calls are always placed to the phone number on the account the token belongs to, so an arbitrary number cannot be dialled.",
    "contact": { "name": "DoNotify", "url": "https://donotifys.com/ai-agents", "email": "info@donotifys.com" }
  },
  "servers": [{ "url": "https://donotifys.com" }],
  "security": [{ "bearerAuth": [] }],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API token created by the user in their DoNotify profile."
      }
    }
  },
  "paths": {
    "/api/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Check the plan, calls remaining, and whether a phone number is set",
        "description": "Call this before placing a call. If phone_number_set is false, tell the user to add their number instead of attempting a call. If remaining is 0, tell them this month's calls are used up.",
        "responses": {
          "200": {
            "description": "Current usage",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plan": { "type": "string", "example": "starter" },
                    "notification_limit": { "type": "integer", "example": 30 },
                    "used_this_month": { "type": "integer", "example": 5 },
                    "remaining": { "type": "integer", "example": 25 },
                    "phone_number_set": { "type": "boolean", "example": true }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/call-now": {
      "post": {
        "operationId": "callNow",
        "summary": "Ring the user's phone immediately",
        "description": "Places a call straight away. The title is spoken first, then the description, so write both as speech rather than as labels.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["title"],
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Spoken aloud first. Write it as a sentence a person would say.",
                    "example": "Your dentist appointment is in thirty minutes"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "Spoken after the title.",
                    "example": "Northside Dental, 40 Queen Street"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Call placed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "reminder_id": { "type": "integer" },
                    "call_uuid": { "type": "string" },
                    "status": { "type": "string" }
                  }
                }
              }
            }
          },
          "422": { "description": "No phone number configured on the account" },
          "500": { "description": "The call could not be placed. Tell the user plainly rather than retrying in a loop." }
        }
      }
    },
    "/api/reminders": {
      "get": {
        "operationId": "listReminders",
        "summary": "List upcoming scheduled calls",
        "description": "Up to 25 pending calls, soonest first. A call that has already been placed is no longer pending.",
        "responses": {
          "200": {
            "description": "Pending calls",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reminders": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": { "type": "integer" },
                          "title": { "type": "string" },
                          "description": { "type": "string", "nullable": true },
                          "call_at": { "type": "string", "format": "date-time" },
                          "event_time": { "type": "string", "format": "date-time", "nullable": true },
                          "status": { "type": "string" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "scheduleCall",
        "summary": "Schedule a phone call for a future time",
        "description": "Convert whatever the user said into ISO 8601 yourself, in their timezone. If their wording is ambiguous, pick a sensible time and say which one you picked.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["title", "call_at"],
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Spoken aloud when the phone is answered.",
                    "example": "Take your blood pressure tablets"
                  },
                  "call_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the phone rings. Must be in the future.",
                    "example": "2026-10-01T08:00:00Z"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 1000,
                    "example": "The white box on the kitchen counter"
                  },
                  "event_time": {
                    "type": "string",
                    "format": "date-time",
                    "description": "The thing being remembered, if it differs from when the phone rings."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Call scheduled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "reminder": {
                      "type": "object",
                      "properties": {
                        "id": { "type": "integer" },
                        "title": { "type": "string" },
                        "description": { "type": "string", "nullable": true },
                        "call_at": { "type": "string", "format": "date-time" },
                        "event_time": { "type": "string", "format": "date-time", "nullable": true },
                        "status": { "type": "string" }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": { "description": "Validation failed, most often a call_at in the past" }
        }
      }
    }
  }
}
