openapi: 3.1.0
info:
  title: Webhooky API
  version: "1.0"
  description: |
    Webhooky turns webhooks and plain HTTP POST requests into push notifications
    on your phone. Create an endpoint in the Webhooky app
    (https://webhooky.app) to get a personal endpoint URL – the long random key
    in the path acts as the credential. There are no API keys and no OAuth.

    The optional JSON body fields `title`, `message`, `sound` and `vibrate`
    override the endpoint's configured defaults per request. All other body
    fields are ignored for display, but stored with the event if the endpoint
    has "store payload" enabled.
  contact:
    name: Webhooky Support
    email: support@webhooky.app
    url: https://webhooky.app
servers:
  - url: https://api.webhooky.app
paths:
  /{key}:
    parameters:
      - name: key
        in: path
        required: true
        description: Your endpoint key from the Webhooky app. Treat it like a password.
        schema:
          type: string
    post:
      operationId: sendNotification
      summary: Send a push notification
      description: >
        Triggers a push notification on every device signed in to the
        endpoint's account. The body is optional; without it, the endpoint's
        configured title, message, sound and vibration are used.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/NotificationOverrides"
            example:
              title: "New order 🎉"
              message: "Order #1042 · €49.99"
              sound: cash_register
              vibrate: true
      responses:
        "200":
          description: >
            Webhook accepted. If the notification was dispatched, the body has
            success: true. If the free plan limit (100 notifications) is
            reached, the webhook is still answered with 200 so senders like
            Stripe don't retry – the body then has success: false and
            reason: free_limit_reached, and the response carries the header
            X-Webhooky-Limit: free-limit-reached.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SendResult"
              examples:
                delivered:
                  summary: Notification delivered
                  value:
                    success: true
                    message: Webhook received and notification sent
                    delivered: 1
                    devices: 1
                freeLimitReached:
                  summary: Accepted but not delivered (free plan limit)
                  value:
                    success: false
                    accepted: true
                    delivered: 0
                    reason: free_limit_reached
                    message: "Webhook accepted, but no notification was delivered: the free plan limit (100 notifications) is reached. Upgrade to Premium in the Webhooky app for unlimited notifications."
        "400":
          description: No key present in the URL.
        "404":
          description: Key not found – the endpoint was deleted or the URL is wrong.
        "405":
          description: Method not allowed – only GET and POST are supported.
    get:
      operationId: getTestPage
      summary: Interactive test page
      description: >
        Returns an HTML test page for this endpoint with input fields for
        title, message, sound and vibration and a send button.
      responses:
        "200":
          description: HTML test page.
          content:
            text/html: {}
        "404":
          description: Key not found.
components:
  schemas:
    NotificationOverrides:
      type: object
      description: All fields are optional; omitted fields fall back to the endpoint's configuration.
      properties:
        title:
          type: string
          maxLength: 100
          description: Replaces the endpoint name as the notification title.
        message:
          type: string
          maxLength: 500
          description: Replaces the configured notification text.
        sound:
          type: string
          description: Sound id for this notification. Unknown ids are ignored.
          enum:
            - default
            - bell_ding
            - cash_register
            - ka_ching
            - coin
            - coin_clatter
            - coin_collision
            - coin_drop
            - coin_received
            - dog_bark
            - fireworks
            - level_up_1
            - level_up_2
            - level_up_3
            - news_ting
            - notification_1
            - notification_2
            - notification_3
            - notification_4
            - notification_5
            - church_bell
            - error_1
            - error_2
            - error_3
            - buzzer
            - car_horn_1
            - car_horn_2
            - doorbell_1
            - doorbell_2
            - doorbell_3
            - doorbell_4
            - cat_meow_1
            - cat_meow_2
            - casino_bling
            - level_complete
            - bonus_reached
            - game_over
            - rubber_duck
            - uplifting_bells
            - bottles_clink
            - coin_win
        vibrate:
          type: boolean
          description: Overrides the endpoint's vibration setting for this notification.
      additionalProperties: true
    SendResult:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        delivered:
          type: integer
          description: Number of devices the notification was delivered to.
        devices:
          type: integer
          description: Number of devices registered on the account.
        accepted:
          type: boolean
          description: Present when the webhook was accepted but not delivered.
        reason:
          type: string
          description: Machine-readable reason when not delivered, e.g. free_limit_reached.
    ErrorResult:
      type: object
      properties:
        success:
          type: boolean
          const: false
        error:
          type: string
