{
  "openapi": "3.1.0",
  "info": {
    "title": "SignOps API",
    "version": "1.0.0",
    "description": "Push notifications to Android, iOS and the web, login approval on a trusted phone, passkeys, authenticator codes and email OTP, on one API key and your own user ids. Errors are { error: { code, message } }. Test keys (sk_test_) send only to test devices and addresses and are not billed.",
    "contact": {
      "email": "hello@signops.dev",
      "url": "https://signops.dev/docs/"
    },
    "termsOfService": "https://signops.dev/terms/"
  },
  "externalDocs": {
    "description": "Docs",
    "url": "https://signops.dev/docs/"
  },
  "servers": [
    {
      "url": "https://api.signops.dev"
    }
  ],
  "security": [
    {
      "secretKey": []
    }
  ],
  "tags": [
    {
      "name": "Devices"
    },
    {
      "name": "Push"
    },
    {
      "name": "Topics"
    },
    {
      "name": "Templates"
    },
    {
      "name": "Approve"
    },
    {
      "name": "Approve SDK"
    },
    {
      "name": "Passkeys"
    },
    {
      "name": "Passkeys browser"
    },
    {
      "name": "Authenticator"
    },
    {
      "name": "Email OTP"
    },
    {
      "name": "Users and DPDP"
    },
    {
      "name": "Verify"
    },
    {
      "name": "Inbox"
    },
    {
      "name": "Live Activities"
    },
    {
      "name": "Status"
    }
  ],
  "paths": {
    "/v1/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Health",
        "description": "200 with ok: true when the database answers, the scheduler ran in the last five minutes and no send is stuck; 503 otherwise.",
        "tags": [
          "Status"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Whether every check passed."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/devices": {
      "post": {
        "operationId": "postDevices",
        "summary": "Add a device",
        "description": "Adds an FCM, APNs or web push token under your user id. The same token again updates it, and moves it if the user changed.",
        "tags": [
          "Devices"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  },
                  "token": {
                    "type": "string",
                    "description": "The FCM token, the APNs device token in hex, or the web push subscription as a JSON string."
                  },
                  "platform": {
                    "type": "string",
                    "description": "Where the token came from.",
                    "enum": [
                      "android",
                      "ios",
                      "web"
                    ]
                  },
                  "name": {
                    "type": "string",
                    "description": "A name for the device, up to 80 characters.",
                    "maxLength": 80
                  }
                },
                "required": [
                  "user_id",
                  "token",
                  "platform"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Device id."
                    },
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "platform": {
                      "type": "string",
                      "description": "android, ios or web."
                    },
                    "route": {
                      "type": "string",
                      "description": "How it is sent: fcm, apns or webpush."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "getDevices",
        "summary": "List a user's devices",
        "tags": [
          "Devices"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Your own id for the user, up to 128 characters without spaces.",
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Device"
                      },
                      "description": "The user's devices, most recently seen first."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user_id}/devices": {
      "get": {
        "operationId": "getUsersByUserIdDevices",
        "summary": "List a user's devices (by path)",
        "tags": [
          "Devices"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Device"
                      },
                      "description": "The user's devices."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/devices/{id}": {
      "delete": {
        "operationId": "deleteDevicesById",
        "summary": "Remove a device",
        "tags": [
          "Devices"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Device id."
                    },
                    "removed": {
                      "type": "boolean",
                      "description": "Always true."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/devices/import": {
      "post": {
        "operationId": "postDevicesImport",
        "summary": "Import devices",
        "description": "Moves tokens from OneSignal or Firebase, up to 500 a call. Each row is checked on its own.",
        "tags": [
          "Devices"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "devices": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "user_id": {
                          "type": "string",
                          "description": "Your own id for the user, up to 128 characters without spaces.",
                          "maxLength": 128
                        },
                        "platform": {
                          "type": "string",
                          "description": "android, ios or web.",
                          "enum": [
                            "android",
                            "ios",
                            "web"
                          ]
                        },
                        "token": {
                          "type": "string",
                          "description": "The token."
                        },
                        "name": {
                          "type": "string",
                          "description": "Optional device name."
                        }
                      },
                      "required": [
                        "user_id",
                        "platform",
                        "token"
                      ]
                    },
                    "description": "Up to 500 devices.",
                    "maxItems": 500
                  }
                },
                "required": [
                  "devices"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "imported": {
                      "type": "integer",
                      "description": "New devices."
                    },
                    "updated": {
                      "type": "integer",
                      "description": "Tokens already known."
                    },
                    "skipped": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "row": {
                            "type": "integer",
                            "description": "Row number."
                          },
                          "reason": {
                            "type": "string",
                            "description": "Why it was skipped."
                          }
                        }
                      },
                      "description": "Up to 100 skipped rows."
                    },
                    "skipped_count": {
                      "type": "integer",
                      "description": "All skipped rows."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/push": {
      "post": {
        "operationId": "postPush",
        "summary": "Send a push",
        "description": "Send to exactly one of user_id, user_ids, topic or all. A push to one user_id, now, is sent before the API answers, with the real result; anything else answers sending or scheduled and reports through webhooks.",
        "tags": [
          "Push"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PushRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PushResult"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "getPush",
        "summary": "List pushes",
        "description": "Newest first. On Free only scheduled and sending pushes are listed.",
        "tags": [
          "Push"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "description": "1 to 100, default 20.",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "The next value from the previous page."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PushSummary"
                      },
                      "description": "Pushes."
                    },
                    "next": {
                      "type": "string",
                      "description": "Pass as before for the next page, or null."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/push/{id}": {
      "get": {
        "operationId": "getPushById",
        "summary": "Get a push",
        "tags": [
          "Push"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PushDetail"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deletePushById",
        "summary": "Cancel a scheduled push",
        "tags": [
          "Push"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Push id."
                    },
                    "status": {
                      "type": "string",
                      "description": "cancelled"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/push/{id}/opened": {
      "post": {
        "operationId": "postPushByIdOpened",
        "summary": "Report an open",
        "description": "Called by the SDKs when the user opens the push or taps a button. Every push carries its id as data.signops_id.",
        "tags": [
          "Push"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "description": "The id of the tapped button, if any."
                  },
                  "platform": {
                    "type": "string",
                    "description": "android, ios or web."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Push id."
                    },
                    "opened": {
                      "type": "integer",
                      "description": "Opens so far, or null."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/topics": {
      "get": {
        "operationId": "getTopics",
        "summary": "List topics",
        "tags": [
          "Topics"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string",
                            "description": "Topic name."
                          },
                          "users": {
                            "type": "integer",
                            "description": "Users in it."
                          }
                        }
                      },
                      "description": "Up to 1,000 topics, largest first."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/topics/{name}/subscribe": {
      "post": {
        "operationId": "postTopicsByNameSubscribe",
        "summary": "Add a user to a topic",
        "description": "A topic is made the first time someone is added.",
        "tags": [
          "Topics"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  }
                },
                "required": [
                  "user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "topic": {
                      "type": "string",
                      "description": "Topic."
                    },
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "subscribed": {
                      "type": "boolean",
                      "description": "true"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/topics/{name}/unsubscribe": {
      "post": {
        "operationId": "postTopicsByNameUnsubscribe",
        "summary": "Take a user out of a topic",
        "tags": [
          "Topics"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  }
                },
                "required": [
                  "user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "topic": {
                      "type": "string",
                      "description": "Topic."
                    },
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "subscribed": {
                      "type": "boolean",
                      "description": "false"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/templates": {
      "get": {
        "operationId": "getTemplates",
        "summary": "List templates",
        "tags": [
          "Templates"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Template"
                      },
                      "description": "Templates."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "postTemplates",
        "summary": "Create or replace a template",
        "description": "Put {{field}} where a value goes, or {{field|default}} for a fallback. Fields work in the title, body, image, link and data.",
        "tags": [
          "Templates"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Up to 64 lowercase letters, digits, dots, dashes or underscores.",
                    "pattern": "^[a-z0-9_.-]{1,64}$"
                  },
                  "title": {
                    "type": "string",
                    "description": "Up to 200 characters.",
                    "maxLength": 200
                  },
                  "body": {
                    "type": "string",
                    "description": "Up to 1,000 characters.",
                    "maxLength": 1000
                  },
                  "image": {
                    "type": "string",
                    "description": "An https URL, or a field like {{image}}."
                  },
                  "link": {
                    "type": "string",
                    "description": "A deep link or URL."
                  },
                  "data": {
                    "type": "object",
                    "properties": {},
                    "description": "Key and value pairs, values may hold fields."
                  },
                  "description": {
                    "type": "string",
                    "description": "A note for your team, up to 200 characters."
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Template"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/templates/{name}": {
      "get": {
        "operationId": "getTemplatesByName",
        "summary": "Get a template",
        "tags": [
          "Templates"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Template"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteTemplatesByName",
        "summary": "Delete a template",
        "tags": [
          "Templates"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "Template name."
                    },
                    "deleted": {
                      "type": "boolean",
                      "description": "true"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/approve/enroll-token": {
      "post": {
        "operationId": "postApproveEnrollToken",
        "summary": "Make an enrol token",
        "description": "After your own login, make this token and pass it to the SDK in your app, which enrols the phone as trusted. Valid 10 minutes.",
        "tags": [
          "Approve"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  }
                },
                "required": [
                  "user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Token"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/approve": {
      "post": {
        "operationId": "postApprove",
        "summary": "Ask the user's trusted phone to approve",
        "description": "Show the returned match number on the screen asking. Pending until the user answers or 2 minutes pass; expired requests are not billed.",
        "tags": [
          "Approve"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  },
                  "context": {
                    "type": "string",
                    "description": "What is being approved, shown on the phone, like Sign in from Chrome on Windows."
                  },
                  "ip": {
                    "type": "string",
                    "description": "The user's IP address, for the per-IP limit and risk signals."
                  },
                  "city": {
                    "type": "string",
                    "description": "Shown on the phone."
                  },
                  "browser": {
                    "type": "string",
                    "description": "Shown on the phone."
                  }
                },
                "required": [
                  "user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Approval id."
                    },
                    "status": {
                      "type": "string",
                      "description": "pending"
                    },
                    "match": {
                      "type": "string",
                      "description": "The number to show on your page."
                    },
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "expires_at": {
                      "type": "string",
                      "description": "ISO 8601 time.",
                      "format": "date-time"
                    },
                    "devices": {
                      "type": "integer",
                      "description": "Trusted devices asked."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/approve/{id}": {
      "get": {
        "operationId": "getApproveById",
        "summary": "Get an approval",
        "tags": [
          "Approve"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "description": "Seconds to hold the connection until the status changes, up to 120.",
              "minimum": 0,
              "maximum": 120
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Approval"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/approve/{id}/cancel": {
      "post": {
        "operationId": "postApproveByIdCancel",
        "summary": "Cancel a pending approval",
        "tags": [
          "Approve"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Approval"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/approve/devices/{id}/remove": {
      "post": {
        "operationId": "postApproveDevicesByIdRemove",
        "summary": "Stop trusting a phone",
        "tags": [
          "Approve"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Device id."
                    },
                    "trusted": {
                      "type": "boolean",
                      "description": "false"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/approve/devices/enroll": {
      "post": {
        "operationId": "postApproveDevicesEnroll",
        "summary": "SDK: enrol a device's public key",
        "description": "Called by the Android, iOS, React Native and Flutter SDKs with the enrol token. The private key stays in the Keystore or Secure Enclave.",
        "tags": [
          "Approve SDK"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "The enrol token from your server."
                  },
                  "public_key": {
                    "type": "string",
                    "description": "P-256 public key, SPKI, base64url."
                  },
                  "platform": {
                    "type": "string",
                    "description": "android or ios."
                  },
                  "push_token": {
                    "type": "string",
                    "description": "The device's push token."
                  },
                  "name": {
                    "type": "string",
                    "description": "Device name."
                  }
                },
                "required": [
                  "token",
                  "public_key",
                  "platform"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/approve/devices/push-token": {
      "post": {
        "operationId": "postApproveDevicesPushToken",
        "summary": "SDK: move to a new push token",
        "description": "Signed by the device key.",
        "tags": [
          "Approve SDK"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "device_id": {
                    "type": "string",
                    "description": "Device id."
                  },
                  "push_token": {
                    "type": "string",
                    "description": "New token."
                  },
                  "timestamp": {
                    "type": "string",
                    "description": "Signing time."
                  },
                  "signature": {
                    "type": "string",
                    "description": "Device signature."
                  }
                },
                "required": [
                  "device_id",
                  "push_token",
                  "timestamp",
                  "signature"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/approve/pending": {
      "get": {
        "operationId": "getApprovePending",
        "summary": "SDK: requests waiting for this device",
        "description": "Signed by the device key in the SignOps-Device, SignOps-Timestamp and SignOps-Signature headers.",
        "tags": [
          "Approve SDK"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "parameters": [
          {
            "name": "SignOps-Device",
            "in": "header",
            "required": true,
            "description": "Device id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "SignOps-Timestamp",
            "in": "header",
            "required": true,
            "description": "Signing time.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "SignOps-Signature",
            "in": "header",
            "required": true,
            "description": "Device signature.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/approve/{id}/respond": {
      "post": {
        "operationId": "postApproveByIdRespond",
        "summary": "SDK: approve or deny",
        "description": "Signed by the device key after number matching and a biometric.",
        "tags": [
          "Approve SDK"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "device_id": {
                    "type": "string",
                    "description": "Device id."
                  },
                  "decision": {
                    "type": "string",
                    "description": "approve or deny.",
                    "enum": [
                      "approve",
                      "deny"
                    ]
                  },
                  "picked": {
                    "type": "string",
                    "description": "The number the user picked."
                  },
                  "timestamp": {
                    "type": "string",
                    "description": "Signing time."
                  },
                  "signature": {
                    "type": "string",
                    "description": "Device signature."
                  }
                },
                "required": [
                  "device_id",
                  "decision",
                  "timestamp",
                  "signature"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/passkeys/registration-token": {
      "post": {
        "operationId": "postPasskeysRegistrationToken",
        "summary": "Make a registration token",
        "description": "For a signed-in user. Pass it to @signops/web or the app SDK, which makes the passkey on your domain. Valid 10 minutes.",
        "tags": [
          "Passkeys"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  },
                  "user_name": {
                    "type": "string",
                    "description": "Shown in the passkey prompt, like an email."
                  },
                  "display_name": {
                    "type": "string",
                    "description": "The user's name."
                  },
                  "rp_id": {
                    "type": "string",
                    "description": "Your domain, when the app has more than one."
                  }
                },
                "required": [
                  "user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Token"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/passkeys/register/options": {
      "post": {
        "operationId": "postPasskeysRegisterOptions",
        "summary": "Browser: registration options",
        "tags": [
          "Passkeys browser"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Registration token."
                  },
                  "platform": {
                    "type": "string",
                    "description": "web, android or ios."
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/passkeys/register/verify": {
      "post": {
        "operationId": "postPasskeysRegisterVerify",
        "summary": "Browser: finish registration",
        "tags": [
          "Passkeys browser"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Registration token."
                  },
                  "response": {
                    "type": "object",
                    "properties": {},
                    "description": "The WebAuthn attestation response."
                  },
                  "name": {
                    "type": "string",
                    "description": "Passkey name."
                  },
                  "platform": {
                    "type": "string",
                    "description": "web, android or ios."
                  }
                },
                "required": [
                  "token",
                  "response"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/passkeys/signin/options": {
      "post": {
        "operationId": "postPasskeysSigninOptions",
        "summary": "Browser: sign-in options",
        "tags": [
          "Passkeys browser"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Optional: limit to this user's passkeys."
                  },
                  "platform": {
                    "type": "string",
                    "description": "web, android or ios."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/passkeys/signin/verify": {
      "post": {
        "operationId": "postPasskeysSigninVerify",
        "summary": "Browser: finish sign-in",
        "description": "Answers a short-lived signops_token that your server confirms with POST /v1/passkeys/verify.",
        "tags": [
          "Passkeys browser"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "session": {
                    "type": "string",
                    "description": "From sign-in options."
                  },
                  "response": {
                    "type": "object",
                    "properties": {},
                    "description": "The WebAuthn assertion response."
                  },
                  "platform": {
                    "type": "string",
                    "description": "web, android or ios."
                  }
                },
                "required": [
                  "session",
                  "response"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/passkeys/verify": {
      "post": {
        "operationId": "postPasskeysVerify",
        "summary": "Confirm a passkey sign-in",
        "description": "Check the signops_token from the browser before starting a session.",
        "tags": [
          "Passkeys"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "signops_token": {
                    "type": "string",
                    "description": "From the browser's sign-in."
                  }
                },
                "required": [
                  "signops_token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "valid": {
                      "type": "boolean",
                      "description": "Whether the sign-in is good."
                    },
                    "verified": {
                      "type": "boolean",
                      "description": "Same as valid."
                    },
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "credential_id": {
                      "type": "string",
                      "description": "The passkey used."
                    },
                    "signals": {
                      "type": "object",
                      "properties": {},
                      "description": "Risk signals, when turned on (Starter and Pro)."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/passkeys": {
      "get": {
        "operationId": "getPasskeys",
        "summary": "List a user's passkeys",
        "tags": [
          "Passkeys"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Your own id for the user, up to 128 characters without spaces.",
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Passkey"
                      },
                      "description": "Passkeys."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user_id}/passkeys": {
      "get": {
        "operationId": "getUsersByUserIdPasskeys",
        "summary": "List a user's passkeys (by path)",
        "tags": [
          "Passkeys"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Passkey"
                      },
                      "description": "Passkeys."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/passkeys/{id}": {
      "delete": {
        "operationId": "deletePasskeysById",
        "summary": "Remove a passkey",
        "tags": [
          "Passkeys"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Passkey id."
                    },
                    "removed": {
                      "type": "boolean",
                      "description": "true"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/totp/enroll": {
      "post": {
        "operationId": "postTotpEnroll",
        "summary": "Start authenticator enrolment",
        "description": "Returns the key, an otpauth link and a QR code as SVG for your page. Not kept for idempotency or logs.",
        "tags": [
          "Authenticator"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  },
                  "label": {
                    "type": "string",
                    "description": "Shown in the authenticator app, like the user's email."
                  },
                  "issuer": {
                    "type": "string",
                    "description": "Your app's name in the authenticator app."
                  }
                },
                "required": [
                  "user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "status": {
                      "type": "string",
                      "description": "pending"
                    },
                    "secret": {
                      "type": "string",
                      "description": "Base32 key."
                    },
                    "otpauth_uri": {
                      "type": "string",
                      "description": "otpauth:// link."
                    },
                    "qr_svg": {
                      "type": "string",
                      "description": "QR code as SVG."
                    },
                    "digits": {
                      "type": "integer",
                      "description": "6"
                    },
                    "period": {
                      "type": "integer",
                      "description": "30"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/totp/confirm": {
      "post": {
        "operationId": "postTotpConfirm",
        "summary": "Confirm with the first code",
        "tags": [
          "Authenticator"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  },
                  "code": {
                    "type": "string",
                    "description": "6-digit code from the app."
                  }
                },
                "required": [
                  "user_id",
                  "code"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "status": {
                      "type": "string",
                      "description": "confirmed"
                    },
                    "backup_codes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "description": "Backup code."
                      },
                      "description": "10 backup codes, shown once."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/totp/verify": {
      "post": {
        "operationId": "postTotpVerify",
        "summary": "Check a code",
        "description": "An app code or a backup code. Five wrong codes in 15 minutes lock the user (429 user_locked).",
        "tags": [
          "Authenticator"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  },
                  "code": {
                    "type": "string",
                    "description": "App code or backup code."
                  }
                },
                "required": [
                  "user_id",
                  "code"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "valid": {
                      "type": "boolean",
                      "description": "Whether the code was right."
                    },
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "method": {
                      "type": "string",
                      "description": "totp or backup_code, null when wrong."
                    },
                    "backup_codes_left": {
                      "type": "integer",
                      "description": "Backup codes left."
                    },
                    "failures_before_lock": {
                      "type": "integer",
                      "description": "When wrong: tries left before the lock."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/totp/backup-codes": {
      "post": {
        "operationId": "postTotpBackupCodes",
        "summary": "New backup codes",
        "description": "The old codes stop working.",
        "tags": [
          "Authenticator"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  }
                },
                "required": [
                  "user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "backup_codes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "description": "Backup code."
                      },
                      "description": "10 new codes."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user_id}/totp": {
      "get": {
        "operationId": "getUsersByUserIdTotp",
        "summary": "Authenticator status",
        "tags": [
          "Authenticator"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteUsersByUserIdTotp",
        "summary": "Remove the authenticator",
        "description": "For a lost phone.",
        "tags": [
          "Authenticator"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "removed": {
                      "type": "boolean",
                      "description": "true"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user_id}/lock": {
      "get": {
        "operationId": "getUsersByUserIdLock",
        "summary": "Lock status",
        "tags": [
          "Authenticator"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "locked": {
                      "type": "boolean",
                      "description": "Whether the user is locked."
                    },
                    "locked_until": {
                      "type": "string",
                      "description": "ISO 8601 time.",
                      "format": "date-time"
                    },
                    "failures": {
                      "type": "integer",
                      "description": "Wrong codes in the current window."
                    },
                    "failures_before_lock": {
                      "type": "integer",
                      "description": "5"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteUsersByUserIdLock",
        "summary": "Unlock the user",
        "tags": [
          "Authenticator"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "locked": {
                      "type": "boolean",
                      "description": "false"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user_id}": {
      "delete": {
        "operationId": "deleteUsersByUserId",
        "summary": "Erase a user",
        "description": "Removes everything held under the id in one transaction. 404 when nothing is held.",
        "tags": [
          "Users and DPDP"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "deleted": {
                      "type": "boolean",
                      "description": "true"
                    },
                    "removed": {
                      "type": "object",
                      "properties": {},
                      "description": "Counts of what was removed."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user_id}/export": {
      "get": {
        "operationId": "getUsersByUserIdExport",
        "summary": "Export a user",
        "description": "Devices, topics, passkeys, authenticator status, approvals of the last 90 days, consent history and pushes, as one JSON document.",
        "tags": [
          "Users and DPDP"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user_id}/consent": {
      "post": {
        "operationId": "postUsersByUserIdConsent",
        "summary": "Record consent",
        "tags": [
          "Users and DPDP"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "purpose": {
                    "type": "string",
                    "description": "Your own word: lowercase letters, digits and underscores, like marketing_push."
                  },
                  "granted": {
                    "type": "boolean",
                    "description": "Yes or no."
                  },
                  "source": {
                    "type": "string",
                    "description": "Where it was given, like signup_form."
                  },
                  "text": {
                    "type": "string",
                    "description": "The words the user agreed to."
                  }
                },
                "required": [
                  "purpose",
                  "granted"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "purpose": {
                      "type": "string",
                      "description": "Purpose."
                    },
                    "granted": {
                      "type": "boolean",
                      "description": "Answer."
                    },
                    "source": {
                      "type": "string",
                      "description": "Source."
                    },
                    "at": {
                      "type": "string",
                      "description": "ISO 8601 time.",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "getUsersByUserIdConsent",
        "summary": "Get consent",
        "tags": [
          "Users and DPDP"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "current": {
                      "type": "object",
                      "properties": {},
                      "description": "Latest answer per purpose."
                    },
                    "history": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "purpose": {
                            "type": "string",
                            "description": "Purpose."
                          },
                          "granted": {
                            "type": "boolean",
                            "description": "Answer."
                          },
                          "source": {
                            "type": "string",
                            "description": "Source."
                          },
                          "text": {
                            "type": "string",
                            "description": "Text."
                          },
                          "at": {
                            "type": "string",
                            "description": "ISO 8601 time.",
                            "format": "date-time"
                          }
                        }
                      },
                      "description": "Every record, in order."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/otp/send": {
      "post": {
        "operationId": "postOtpSend",
        "summary": "Send an email code",
        "description": "Makes a 6-digit code, valid 10 minutes, 5 tries. Live codes go from your verified domain; test keys send to your test addresses.",
        "tags": [
          "Email OTP"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "description": "The address to send to.",
                    "format": "email"
                  },
                  "app_name": {
                    "type": "string",
                    "description": "Shown in the email, up to 40 characters, no web or email addresses."
                  },
                  "language": {
                    "type": "string",
                    "description": "en or hi.",
                    "enum": [
                      "en",
                      "hi"
                    ]
                  },
                  "ip": {
                    "type": "string",
                    "description": "The user's IP address, for the per-IP limit and risk signals."
                  }
                },
                "required": [
                  "email"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "otp_id": {
                      "type": "string",
                      "description": "Code id, for verify."
                    },
                    "status": {
                      "type": "string",
                      "description": "sent"
                    },
                    "email": {
                      "type": "string",
                      "description": "The address to send to.",
                      "format": "email"
                    },
                    "expires_at": {
                      "type": "string",
                      "description": "ISO 8601 time.",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/otp/verify": {
      "post": {
        "operationId": "postOtpVerify",
        "summary": "Check an email code",
        "tags": [
          "Email OTP"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "otp_id": {
                    "type": "string",
                    "description": "From send."
                  },
                  "code": {
                    "type": "string",
                    "description": "The 6 digits the user typed."
                  }
                },
                "required": [
                  "otp_id",
                  "code"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "valid": {
                      "type": "boolean",
                      "description": "Right or wrong."
                    },
                    "otp_id": {
                      "type": "string",
                      "description": "Code id."
                    },
                    "email": {
                      "type": "string",
                      "description": "When right: the address."
                    },
                    "tries_left": {
                      "type": "integer",
                      "description": "When wrong."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/otp/{id}": {
      "get": {
        "operationId": "getOtpById",
        "summary": "Get a code's status",
        "tags": [
          "Email OTP"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/magic-links": {
      "post": {
        "operationId": "postMagicLinks",
        "summary": "Email a sign-in link",
        "description": "The link opens redirect_url with signops_token added. Valid 15 minutes, once.",
        "tags": [
          "Email OTP"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "description": "The address to send to.",
                    "format": "email"
                  },
                  "redirect_url": {
                    "type": "string",
                    "description": "https on your verified domain or a listed host; http://localhost with a test key."
                  },
                  "app_name": {
                    "type": "string",
                    "description": "Shown in the email."
                  },
                  "language": {
                    "type": "string",
                    "description": "en or hi.",
                    "enum": [
                      "en",
                      "hi"
                    ]
                  },
                  "ip": {
                    "type": "string",
                    "description": "The user's IP address, for the per-IP limit and risk signals."
                  }
                },
                "required": [
                  "email",
                  "redirect_url"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "link_id": {
                      "type": "string",
                      "description": "Link id."
                    },
                    "status": {
                      "type": "string",
                      "description": "sent"
                    },
                    "email": {
                      "type": "string",
                      "description": "The address to send to.",
                      "format": "email"
                    },
                    "redirect_url": {
                      "type": "string",
                      "description": "Where the link opens."
                    },
                    "expires_at": {
                      "type": "string",
                      "description": "ISO 8601 time.",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/magic-links/verify": {
      "post": {
        "operationId": "postMagicLinksVerify",
        "summary": "Check a link's token",
        "tags": [
          "Email OTP"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "signops_token from the URL."
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "valid": {
                      "type": "boolean",
                      "description": "Right or wrong."
                    },
                    "link_id": {
                      "type": "string",
                      "description": "Link id."
                    },
                    "email": {
                      "type": "string",
                      "description": "When right."
                    },
                    "redirect_url": {
                      "type": "string",
                      "description": "When right."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/magic-links/{id}": {
      "get": {
        "operationId": "getMagicLinksById",
        "summary": "Get a link's status",
        "tags": [
          "Email OTP"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/verify": {
      "post": {
        "operationId": "postVerify",
        "summary": "Start a verify session (Starter and Pro)",
        "description": "Tries passkey, Approve, authenticator, then email, in that order, keeping only the steps the user can do. Valid 10 minutes.",
        "tags": [
          "Verify"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  },
                  "context": {
                    "type": "string",
                    "description": "Shown on the phone for Approve."
                  },
                  "steps": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "description": "Step.",
                      "enum": [
                        "passkey",
                        "approve",
                        "totp",
                        "email"
                      ]
                    },
                    "description": "Leave steps out; the order never changes."
                  },
                  "email": {
                    "type": "string",
                    "description": "For the email step."
                  },
                  "ip": {
                    "type": "string",
                    "description": "The user's IP address, for the per-IP limit and risk signals."
                  },
                  "user_agent": {
                    "type": "string",
                    "description": "For risk signals."
                  },
                  "browser": {
                    "type": "string",
                    "description": "Shown by Approve."
                  },
                  "city": {
                    "type": "string",
                    "description": "Shown by Approve."
                  },
                  "country": {
                    "type": "string",
                    "description": "For risk signals."
                  },
                  "device_id": {
                    "type": "string",
                    "description": "Your own device id, for new_device."
                  }
                },
                "required": [
                  "user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/verify/{id}": {
      "get": {
        "operationId": "getVerifyById",
        "summary": "Get a verify session",
        "tags": [
          "Verify"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "description": "Seconds to wait on an Approve step."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/verify/{id}/check": {
      "post": {
        "operationId": "postVerifyByIdCheck",
        "summary": "Check the current step",
        "tags": [
          "Verify"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "signops_token": {
                    "type": "string",
                    "description": "For a passkey step."
                  },
                  "code": {
                    "type": "string",
                    "description": "For an authenticator or email step."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/verify/{id}/next": {
      "post": {
        "operationId": "postVerifyByIdNext",
        "summary": "Move to the next step",
        "tags": [
          "Verify"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "description": "Why, up to 120 characters."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/signals": {
      "post": {
        "operationId": "postSignals",
        "summary": "Record a sign-in and get its risk signals (Starter and Pro)",
        "description": "Facts, never a score: first_signin, new_device, new_country, impossible_travel, tor, datacenter. One SignOps cannot know is null.",
        "tags": [
          "Verify"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  },
                  "ip": {
                    "type": "string",
                    "description": "The user's IP address, for the per-IP limit and risk signals."
                  },
                  "user_agent": {
                    "type": "string",
                    "description": "The browser's user agent."
                  },
                  "device_id": {
                    "type": "string",
                    "description": "Your own device id."
                  },
                  "country": {
                    "type": "string",
                    "description": "ISO country code."
                  },
                  "city": {
                    "type": "string",
                    "description": "City."
                  },
                  "lat": {
                    "type": "number",
                    "description": "Latitude."
                  },
                  "lon": {
                    "type": "number",
                    "description": "Longitude."
                  },
                  "email": {
                    "type": "string",
                    "description": "For a new-login alert by email."
                  }
                },
                "required": [
                  "user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "signals": {
                      "type": "object",
                      "properties": {},
                      "description": "Each signal with its source."
                    },
                    "alert": {
                      "type": "object",
                      "properties": {},
                      "description": "Whether a new-login alert went, and how."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/inbox/token": {
      "post": {
        "operationId": "postInboxToken",
        "summary": "Make an inbox token for the app",
        "description": "Starter and Pro. The app sends it as SignOps-Inbox-Token with the publishable key.",
        "tags": [
          "Inbox"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  },
                  "hours": {
                    "type": "integer",
                    "description": "Default 24, up to 168.",
                    "minimum": 1,
                    "maximum": 168
                  }
                },
                "required": [
                  "user_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "token": {
                      "type": "string",
                      "description": "Inbox token."
                    },
                    "expires_at": {
                      "type": "string",
                      "description": "ISO 8601 time.",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/inbox": {
      "post": {
        "operationId": "postInbox",
        "summary": "Store an inbox message without a push",
        "tags": [
          "Inbox"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "user_id": {
                    "type": "string",
                    "description": "Your own id for the user, up to 128 characters without spaces.",
                    "maxLength": 128
                  },
                  "user_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "description": "Your own id for the user, up to 128 characters without spaces.",
                      "maxLength": 128
                    },
                    "description": "Or several users."
                  },
                  "title": {
                    "type": "string",
                    "description": "Title."
                  },
                  "body": {
                    "type": "string",
                    "description": "Text."
                  },
                  "link": {
                    "type": "string",
                    "description": "Link."
                  },
                  "image": {
                    "type": "string",
                    "description": "https image."
                  },
                  "data": {
                    "type": "object",
                    "properties": {},
                    "description": "Key and value pairs."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "getInbox",
        "summary": "App: read the inbox",
        "tags": [
          "Inbox"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "description": "Up to 100."
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "The next value from the previous page."
            }
          },
          {
            "name": "SignOps-Inbox-Token",
            "in": "header",
            "required": true,
            "description": "From POST /v1/inbox/token.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/inbox/read": {
      "post": {
        "operationId": "postInboxRead",
        "summary": "App: mark read",
        "tags": [
          "Inbox"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "parameters": [
          {
            "name": "SignOps-Inbox-Token",
            "in": "header",
            "required": true,
            "description": "From POST /v1/inbox/token.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "description": "Message id."
                    },
                    "description": "Ids to mark."
                  },
                  "all": {
                    "type": "boolean",
                    "description": "Mark everything read."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "read": {
                      "type": "integer",
                      "description": "Messages marked."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/inbox/{id}": {
      "delete": {
        "operationId": "deleteInboxById",
        "summary": "App: remove a message",
        "tags": [
          "Inbox"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "SignOps-Inbox-Token",
            "in": "header",
            "required": true,
            "description": "From POST /v1/inbox/token.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Message id."
                    },
                    "deleted": {
                      "type": "boolean",
                      "description": "true"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user_id}/inbox": {
      "get": {
        "operationId": "getUsersByUserIdInbox",
        "summary": "Server: a user's inbox",
        "tags": [
          "Inbox"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "description": "Up to 100."
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "The next value from the previous page."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user_id}/inbox/read": {
      "post": {
        "operationId": "postUsersByUserIdInboxRead",
        "summary": "Server: mark read",
        "tags": [
          "Inbox"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "description": "Message id."
                    },
                    "description": "Ids to mark."
                  },
                  "all": {
                    "type": "boolean",
                    "description": "Mark everything read."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "read": {
                      "type": "integer",
                      "description": "Messages marked."
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user_id}/inbox/{id}": {
      "delete": {
        "operationId": "deleteUsersByUserIdInboxById",
        "summary": "Server: remove a message",
        "tags": [
          "Inbox"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Message id."
                    },
                    "deleted": {
                      "type": "boolean",
                      "description": "true"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/activities": {
      "post": {
        "operationId": "postActivities",
        "summary": "Start a Live Activity or ongoing notification (Starter and Pro)",
        "tags": [
          "Live Activities"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActivityStart"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/activities/{ref}": {
      "get": {
        "operationId": "getActivitiesByRef",
        "summary": "Get an activity",
        "tags": [
          "Live Activities"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/activities/{ref}/update": {
      "post": {
        "operationId": "postActivitiesByRefUpdate",
        "summary": "Update an activity",
        "tags": [
          "Live Activities"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActivityState"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/activities/{ref}/end": {
      "post": {
        "operationId": "postActivitiesByRefEnd",
        "summary": "End an activity",
        "tags": [
          "Live Activities"
        ],
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "The same key within 24 hours returns the first result instead of acting twice.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActivityState"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/activities/{ref}/token": {
      "post": {
        "operationId": "postActivitiesByRefToken",
        "summary": "iOS SDK: the Live Activity's push token",
        "tags": [
          "Live Activities"
        ],
        "security": [
          {
            "publishableKey": []
          }
        ],
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "push_token": {
                    "type": "string",
                    "description": "ActivityKit push token."
                  }
                },
                "required": [
                  "push_token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ref": {
                      "type": "string",
                      "description": "Your ref."
                    },
                    "ios": {
                      "type": "boolean",
                      "description": "true"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "5XX": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "secretKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "sk_live_... or sk_test_..., on your server only."
      },
      "publishableKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "pk_live_... or pk_test_..., used by the SDKs in apps and browsers."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable code, like invalid_request, rate_limited or expired."
              },
              "message": {
                "type": "string",
                "description": "Written for a developer to read."
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Token": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "Single-use token."
          },
          "expires_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          }
        }
      },
      "Device": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Device id."
          },
          "user_id": {
            "type": "string",
            "description": "Your own id for the user, up to 128 characters without spaces.",
            "maxLength": 128
          },
          "platform": {
            "type": "string",
            "description": "android, ios or web."
          },
          "route": {
            "type": "string",
            "description": "fcm, apns or webpush."
          },
          "name": {
            "type": "string",
            "description": "Device name."
          },
          "trusted": {
            "type": "boolean",
            "description": "Enrolled for Approve."
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          },
          "last_seen_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          }
        }
      },
      "PushRequest": {
        "type": "object",
        "properties": {
          "user_id": {
            "type": "string",
            "description": "Your own id for the user, up to 128 characters without spaces.",
            "maxLength": 128
          },
          "user_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "Your own id for the user, up to 128 characters without spaces.",
              "maxLength": 128
            },
            "description": "Up to 1,000 users.",
            "maxItems": 1000
          },
          "topic": {
            "type": "string",
            "description": "A topic name."
          },
          "all": {
            "type": "boolean",
            "description": "Every device in the app."
          },
          "title": {
            "type": "string",
            "description": "Notification title."
          },
          "body": {
            "type": "string",
            "description": "The text under it."
          },
          "image": {
            "type": "string",
            "description": "https image URL."
          },
          "link": {
            "type": "string",
            "description": "Deep link or URL opened on tap."
          },
          "data": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Key and value pairs your app reads. With no title or body, the push is silent."
          },
          "buttons": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Up to 32 letters, digits, dashes, dots or underscores; reported as the open's action."
                },
                "title": {
                  "type": "string",
                  "description": "Up to 40 characters."
                },
                "link": {
                  "type": "string",
                  "description": "Opened on tap."
                }
              },
              "required": [
                "id",
                "title"
              ]
            },
            "description": "1 to 3 buttons.",
            "minItems": 1,
            "maxItems": 3
          },
          "sound": {
            "description": "A sound file in the app, \"default\", or false for none.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "boolean",
                "const": false
              }
            ]
          },
          "badge": {
            "type": "integer",
            "description": "App icon number, 0 to 99,999. 0 clears it on iOS.",
            "minimum": 0,
            "maximum": 99999
          },
          "android_channel": {
            "type": "string",
            "description": "Android notification channel id."
          },
          "ios_category": {
            "type": "string",
            "description": "iOS notification category the app registered."
          },
          "thread_id": {
            "type": "string",
            "description": "iOS notification group."
          },
          "priority": {
            "type": "string",
            "description": "Default high.",
            "enum": [
              "high",
              "normal"
            ]
          },
          "ttl": {
            "type": "integer",
            "description": "Seconds a push may wait for an offline device, up to 28 days.",
            "minimum": 0,
            "maximum": 2419200
          },
          "collapse_key": {
            "type": "string",
            "description": "Newer pushes with the same key replace older ones, up to 64 characters."
          },
          "send_at": {
            "type": "string",
            "description": "ISO time with a zone, up to 30 days ahead.",
            "format": "date-time"
          },
          "marketing": {
            "type": "boolean",
            "description": "Applies the app's daily cap per user and, when turned on, the consent check."
          },
          "template": {
            "type": "string",
            "description": "A template's name."
          },
          "fields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Values for the template."
          },
          "fields_by_user": {
            "type": "object",
            "additionalProperties": {
              "type": "object"
            },
            "description": "With user_ids: each user's own values, on top of fields."
          },
          "inbox": {
            "type": "boolean",
            "description": "Also keep it in each user's inbox (Starter and Pro)."
          }
        }
      },
      "PushResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Push id."
          },
          "status": {
            "type": "string",
            "description": "sent, sending or scheduled."
          },
          "devices": {
            "type": "integer",
            "description": "Devices targeted."
          },
          "sent": {
            "type": "integer",
            "description": "For one user, now: devices that took it."
          },
          "failed": {
            "type": "integer",
            "description": "For one user, now: devices that refused it."
          },
          "inbox": {
            "type": "object",
            "properties": {},
            "description": "Inbox rows stored, when inbox is true."
          }
        }
      },
      "PushSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Push id."
          },
          "status": {
            "type": "string",
            "description": "Status."
          },
          "template": {
            "type": "string",
            "description": "Template, if any."
          },
          "devices": {
            "type": "integer",
            "description": "Devices."
          },
          "sent": {
            "type": "integer",
            "description": "Sent."
          },
          "failed": {
            "type": "integer",
            "description": "Failed."
          },
          "send_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          }
        }
      },
      "PushDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Push id."
          },
          "status": {
            "type": "string",
            "description": "Status."
          },
          "devices": {
            "type": "integer",
            "description": "Devices."
          },
          "sent": {
            "type": "integer",
            "description": "Sent."
          },
          "failed": {
            "type": "integer",
            "description": "Failed."
          },
          "opened": {
            "type": "integer",
            "description": "Opens reported by the SDKs."
          },
          "send_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          },
          "by_platform": {
            "type": "object",
            "properties": {
              "android": {
                "type": "object",
                "properties": {
                  "sent": {
                    "type": "integer",
                    "description": "Sent."
                  },
                  "failed": {
                    "type": "integer",
                    "description": "Failed."
                  }
                }
              },
              "ios": {
                "type": "object",
                "properties": {
                  "sent": {
                    "type": "integer",
                    "description": "Sent."
                  },
                  "failed": {
                    "type": "integer",
                    "description": "Failed."
                  }
                }
              },
              "web": {
                "type": "object",
                "properties": {
                  "sent": {
                    "type": "integer",
                    "description": "Sent."
                  },
                  "failed": {
                    "type": "integer",
                    "description": "Failed."
                  }
                }
              }
            }
          }
        }
      },
      "Template": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Name."
          },
          "title": {
            "type": "string",
            "description": "Title."
          },
          "body": {
            "type": "string",
            "description": "Body."
          },
          "image": {
            "type": "string",
            "description": "Image."
          },
          "link": {
            "type": "string",
            "description": "Link."
          },
          "data": {
            "type": "object",
            "properties": {}
          },
          "description": {
            "type": "string",
            "description": "Note."
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "Field."
            },
            "description": "The fields it uses."
          }
        }
      },
      "Approval": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Approval id."
          },
          "user_id": {
            "type": "string",
            "description": "Your own id for the user, up to 128 characters without spaces.",
            "maxLength": 128
          },
          "context": {
            "type": "string",
            "description": "What was asked."
          },
          "status": {
            "type": "string",
            "description": "pending, approved, denied, expired or cancelled."
          },
          "device_id": {
            "type": "string",
            "description": "The device that answered."
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          },
          "answered_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          }
        }
      },
      "Passkey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Passkey id."
          },
          "user_id": {
            "type": "string",
            "description": "Your own id for the user, up to 128 characters without spaces.",
            "maxLength": 128
          },
          "synced": {
            "type": "boolean",
            "description": "Backed up to iCloud Keychain or Google Password Manager."
          },
          "name": {
            "type": "string",
            "description": "Name."
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          },
          "last_used_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          }
        }
      },
      "ActivityStart": {
        "type": "object",
        "properties": {
          "ref": {
            "type": "string",
            "description": "Your own id for the activity, like an order id."
          },
          "user_id": {
            "type": "string",
            "description": "Your own id for the user, up to 128 characters without spaces.",
            "maxLength": 128
          },
          "title": {
            "type": "string",
            "description": "Android: title."
          },
          "body": {
            "type": "string",
            "description": "Android: text."
          },
          "progress": {
            "type": "integer",
            "description": "0 to 100 draws a progress bar.",
            "minimum": 0,
            "maximum": 100
          },
          "content_state": {
            "type": "object",
            "properties": {},
            "description": "iOS: the object your ContentState decodes."
          },
          "alert": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "description": "Title."
              },
              "body": {
                "type": "string",
                "description": "Text."
              }
            },
            "description": "iOS: light the screen for this update."
          },
          "stale_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          },
          "dismiss_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          },
          "priority": {
            "type": "string",
            "description": "high sends at Apple's high priority without an alert.",
            "enum": [
              "high"
            ]
          }
        },
        "required": [
          "ref",
          "user_id"
        ]
      },
      "ActivityState": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "description": "Android: title."
          },
          "body": {
            "type": "string",
            "description": "Android: text."
          },
          "progress": {
            "type": "integer",
            "description": "0 to 100 draws a progress bar.",
            "minimum": 0,
            "maximum": 100
          },
          "content_state": {
            "type": "object",
            "properties": {},
            "description": "iOS: the object your ContentState decodes."
          },
          "alert": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "description": "Title."
              },
              "body": {
                "type": "string",
                "description": "Text."
              }
            },
            "description": "iOS: light the screen for this update."
          },
          "stale_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          },
          "dismiss_at": {
            "type": "string",
            "description": "ISO 8601 time.",
            "format": "date-time"
          },
          "priority": {
            "type": "string",
            "description": "high sends at Apple's high priority without an alert.",
            "enum": [
              "high"
            ]
          }
        }
      }
    }
  }
}