{
  "components": {
    "schemas": {
      "DecisionChange": {
        "properties": {
          "changed_at": {
            "format": "date-time",
            "title": "Changed At",
            "type": "string"
          },
          "channel": {
            "title": "Channel",
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "data": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Data"
          },
          "decision": {
            "$ref": "#/components/schemas/SenderDecision"
          },
          "event_id": {
            "title": "Event Id",
            "type": "string"
          },
          "external_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "External Id"
          }
        },
        "required": [
          "event_id",
          "external_id",
          "channel",
          "created_at",
          "changed_at",
          "decision",
          "data"
        ],
        "title": "DecisionChange",
        "type": "object"
      },
      "DecisionChangeList": {
        "properties": {
          "decisions": {
            "items": {
              "$ref": "#/components/schemas/DecisionChange"
            },
            "title": "Decisions",
            "type": "array"
          },
          "has_more": {
            "title": "Has More",
            "type": "boolean"
          },
          "next_cursor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next Cursor"
          }
        },
        "required": [
          "decisions",
          "next_cursor",
          "has_more"
        ],
        "title": "DecisionChangeList",
        "type": "object"
      },
      "DecisionIn": {
        "additionalProperties": false,
        "description": "Documentation-only: runtime validation is `decisions.parse`.",
        "properties": {
          "allow_comment": {
            "default": false,
            "title": "Allow Comment",
            "type": "boolean"
          },
          "confirm_twice": {
            "default": false,
            "description": "Ask again before the vote counts; direct API votes must send confirmed=true.",
            "title": "Confirm Twice",
            "type": "boolean"
          },
          "expires_in": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "\"2h\", \"30m\", \"1d\" or ISO 8601 (PT2H). 5 minutes to 7 days, default 24h.",
            "title": "Expires In"
          },
          "options": {
            "items": {
              "$ref": "#/components/schemas/DecisionOptionIn"
            },
            "maxItems": 5,
            "minItems": 2,
            "title": "Options",
            "type": "array"
          },
          "policy": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Slug or id of a decision policy an owner or admin created in the dashboard (Business plan). It decides who may decide and how many approvals are needed. You can only name a policy, never define one: `roles` and other inline rules return 422. Unknown, archived or other-workspace policies return 422.",
            "title": "Policy"
          },
          "prompt": {
            "maxLength": 200,
            "minLength": 1,
            "title": "Prompt",
            "type": "string"
          },
          "requested_by": {
            "anyOf": [
              {
                "maxLength": 254,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "User id or email of the workspace member who asked for this. A policy with requester_cannot_decide stops that person approving their own request. Not a member: 422.",
            "title": "Requested By"
          },
          "require_device_unlock": {
            "default": false,
            "description": "The mobile app asks for the phone's fingerprint, face or PIN before sending. Checked on the phone only; the server cannot verify it.",
            "title": "Require Device Unlock",
            "type": "boolean"
          },
          "require_note": {
            "default": false,
            "description": "The voter must add a short note (max 500 chars) saying why.",
            "title": "Require Note",
            "type": "boolean"
          }
        },
        "required": [
          "prompt",
          "options"
        ],
        "title": "DecisionIn",
        "type": "object"
      },
      "DecisionOptionIn": {
        "additionalProperties": false,
        "properties": {
          "id": {
            "maxLength": 32,
            "pattern": "^[a-z0-9][a-z0-9_-]{0,31}$",
            "title": "Id",
            "type": "string"
          },
          "label": {
            "maxLength": 40,
            "minLength": 1,
            "title": "Label",
            "type": "string"
          }
        },
        "required": [
          "id",
          "label"
        ],
        "title": "DecisionOptionIn",
        "type": "object"
      },
      "DeliverySummary": {
        "properties": {
          "devices": {
            "default": 0,
            "title": "Devices",
            "type": "integer"
          },
          "failed": {
            "default": 0,
            "title": "Failed",
            "type": "integer"
          },
          "pending": {
            "default": 0,
            "title": "Pending",
            "type": "integer"
          },
          "provider_accepted": {
            "default": 0,
            "title": "Provider Accepted",
            "type": "integer"
          },
          "recipients": {
            "default": 0,
            "title": "Recipients",
            "type": "integer"
          },
          "retrying": {
            "default": 0,
            "title": "Retrying",
            "type": "integer"
          },
          "simulated_accepted": {
            "default": 0,
            "title": "Simulated Accepted",
            "type": "integer"
          },
          "skipped": {
            "default": 0,
            "title": "Skipped",
            "type": "integer"
          }
        },
        "title": "DeliverySummary",
        "type": "object"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "title": "HTTPValidationError",
        "type": "object"
      },
      "NotificationAccepted": {
        "properties": {
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "deduplicated": {
            "title": "Deduplicated",
            "type": "boolean"
          },
          "event_id": {
            "title": "Event Id",
            "type": "string"
          },
          "idempotent_replay": {
            "title": "Idempotent Replay",
            "type": "boolean"
          },
          "status": {
            "enum": [
              "accepted",
              "queued",
              "provider_accepted",
              "simulated_accepted",
              "no_devices",
              "retrying",
              "failed"
            ],
            "title": "Status",
            "type": "string"
          },
          "status_url": {
            "title": "Status Url",
            "type": "string"
          }
        },
        "required": [
          "event_id",
          "status",
          "created_at",
          "status_url",
          "idempotent_replay",
          "deduplicated"
        ],
        "title": "NotificationAccepted",
        "type": "object"
      },
      "NotificationIn": {
        "additionalProperties": false,
        "description": "Documentation-only request model. Runtime validation is `ingest.validate`.\n\nThe route reads the raw body itself (size, charset, strict JSON), so this model is attached\nto the OpenAPI document only.",
        "properties": {
          "action_url": {
            "anyOf": [
              {
                "maxLength": 2048,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Absolute https URL.",
            "title": "Action Url"
          },
          "body": {
            "anyOf": [
              {
                "maxLength": 2000,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Body"
          },
          "channel": {
            "default": "general",
            "maxLength": 40,
            "pattern": "^[a-z0-9][a-z0-9-]{0,39}$",
            "title": "Channel",
            "type": "string"
          },
          "data": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "JSON object, at most 8192 bytes when serialized.",
            "title": "Data"
          },
          "decision": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/DecisionIn"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Attach a question with 2 to 5 options. A permitted team member answers it; poll GET /v1/notifications/{event_id} or GET /v1/decisions for the answer. A decision raises priority 'low' to 'normal'."
          },
          "event_type": {
            "anyOf": [
              {
                "maxLength": 100,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,99}$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Event Type"
          },
          "external_id": {
            "anyOf": [
              {
                "maxLength": 200,
                "minLength": 1,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "External Id"
          },
          "occurred_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Occurred At"
          },
          "priority": {
            "default": "normal",
            "enum": [
              "low",
              "normal",
              "high"
            ],
            "title": "Priority",
            "type": "string"
          },
          "title": {
            "maxLength": 120,
            "minLength": 1,
            "title": "Title",
            "type": "string"
          }
        },
        "required": [
          "title"
        ],
        "title": "NotificationIn",
        "type": "object"
      },
      "NotificationStatus": {
        "properties": {
          "acknowledged": {
            "title": "Acknowledged",
            "type": "boolean"
          },
          "acknowledged_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Acknowledged At"
          },
          "channel": {
            "title": "Channel",
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "dead_lettered_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Dead Lettered At"
          },
          "decision": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SenderDecision"
              },
              {
                "type": "null"
              }
            ]
          },
          "delivery_summary": {
            "$ref": "#/components/schemas/DeliverySummary"
          },
          "event_id": {
            "title": "Event Id",
            "type": "string"
          },
          "external_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "External Id"
          },
          "project_id": {
            "title": "Project Id",
            "type": "string"
          },
          "status": {
            "enum": [
              "accepted",
              "queued",
              "provider_accepted",
              "simulated_accepted",
              "no_devices",
              "retrying",
              "failed"
            ],
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "event_id",
          "project_id",
          "channel",
          "status",
          "created_at",
          "external_id",
          "delivery_summary",
          "acknowledged",
          "acknowledged_at",
          "dead_lettered_at"
        ],
        "title": "NotificationStatus",
        "type": "object"
      },
      "OptionOut": {
        "properties": {
          "id": {
            "title": "Id",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          }
        },
        "required": [
          "id",
          "label"
        ],
        "title": "OptionOut",
        "type": "object"
      },
      "PolicyRef": {
        "description": "The policy version that rules a decision (plan section 5).",
        "properties": {
          "approvals_required": {
            "title": "Approvals Required",
            "type": "integer"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "version": {
            "title": "Version",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "version",
          "approvals_required"
        ],
        "title": "PolicyRef",
        "type": "object"
      },
      "SenderDecision": {
        "description": "What the sending server gets back (plan section 5). Undecided fields are null.",
        "properties": {
          "comment": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Comment"
          },
          "decided_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Decided At"
          },
          "decided_by": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SenderDecisionBy"
              },
              {
                "type": "null"
              }
            ]
          },
          "expired_as": {
            "anyOf": [
              {
                "const": "rejected",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Expired As"
          },
          "expires_at": {
            "format": "date-time",
            "title": "Expires At",
            "type": "string"
          },
          "label": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Label"
          },
          "note": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Note"
          },
          "option_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Option Id"
          },
          "options": {
            "items": {
              "$ref": "#/components/schemas/OptionOut"
            },
            "title": "Options",
            "type": "array"
          },
          "outcome": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SenderOutcome"
              },
              {
                "type": "null"
              }
            ]
          },
          "policy": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PolicyRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "prompt": {
            "title": "Prompt",
            "type": "string"
          },
          "status": {
            "enum": [
              "pending",
              "decided",
              "expired",
              "cancelled"
            ],
            "title": "Status",
            "type": "string"
          },
          "via": {
            "anyOf": [
              {
                "enum": [
                  "web",
                  "mobile",
                  "push_action"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Via"
          },
          "votes": {
            "default": [],
            "items": {
              "$ref": "#/components/schemas/SenderVote"
            },
            "title": "Votes",
            "type": "array"
          }
        },
        "required": [
          "status",
          "prompt",
          "options",
          "expires_at",
          "option_id",
          "label",
          "comment",
          "decided_by",
          "decided_at",
          "via"
        ],
        "title": "SenderDecision",
        "type": "object"
      },
      "SenderDecisionBy": {
        "properties": {
          "name": {
            "title": "Name",
            "type": "string"
          },
          "user_id": {
            "title": "User Id",
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "name"
        ],
        "title": "SenderDecisionBy",
        "type": "object"
      },
      "SenderOutcome": {
        "properties": {
          "label": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Label"
          },
          "option_id": {
            "title": "Option Id",
            "type": "string"
          }
        },
        "required": [
          "option_id",
          "label"
        ],
        "title": "SenderOutcome",
        "type": "object"
      },
      "SenderVote": {
        "description": "One vote as the sender sees it (plan section 5).",
        "properties": {
          "at": {
            "format": "date-time",
            "title": "At",
            "type": "string"
          },
          "comment": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Comment"
          },
          "note": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Note"
          },
          "option_id": {
            "title": "Option Id",
            "type": "string"
          },
          "role": {
            "title": "Role",
            "type": "string"
          },
          "user_id": {
            "title": "User Id",
            "type": "string"
          },
          "via": {
            "enum": [
              "web",
              "mobile",
              "push_action"
            ],
            "title": "Via",
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "role",
          "option_id",
          "comment",
          "at",
          "via"
        ],
        "title": "SenderVote",
        "type": "object"
      },
      "ValidationError": {
        "properties": {
          "ctx": {
            "title": "Context",
            "type": "object"
          },
          "input": {
            "title": "Input"
          },
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "title": "Location",
            "type": "array"
          },
          "msg": {
            "title": "Message",
            "type": "string"
          },
          "type": {
            "title": "Error Type",
            "type": "string"
          }
        },
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError",
        "type": "object"
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "description": "A project API key. Set it from the PINGLOOM_API_KEY environment variable.",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "Send notifications, check their status and poll decision answers with a project API key. Server-to-server only: never put a key in a mobile app or browser code.",
    "title": "PingLoom server API",
    "version": "1.0.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/v1/decisions": {
      "get": {
        "description": "Server API key (events:read). Decisions of the key's project, oldest change first. Pass `next_cursor` back as `since` to receive only newer changes. A decision shows up when created and again, with its new state, after it is decided or expires.",
        "operationId": "list_decisions",
        "parameters": [
          {
            "in": "query",
            "name": "status",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "enum": [
                    "pending",
                    "decided",
                    "expired",
                    "cancelled"
                  ],
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Status"
            }
          },
          {
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "maxLength": 256,
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Since"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DecisionChangeList"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "List Decisions",
        "tags": [
          "decisions"
        ]
      }
    },
    "/v1/notifications": {
      "post": {
        "operationId": "create_notification",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NotificationIn"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationAccepted"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create Notification",
        "tags": [
          "ingest"
        ]
      }
    },
    "/v1/notifications/{event_id}": {
      "get": {
        "operationId": "get_notification_status",
        "parameters": [
          {
            "in": "path",
            "name": "event_id",
            "required": true,
            "schema": {
              "title": "Event Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationStatus"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Get Notification Status",
        "tags": [
          "ingest"
        ]
      }
    }
  },
  "servers": [
    {
      "url": "{baseUrl}",
      "variables": {
        "baseUrl": {
          "default": "http://127.0.0.1:18080"
        }
      }
    }
  ]
}
