{
  "openapi": "3.1.0",
  "info": {
    "title": "Orvain UI website API",
    "version": "1.0.0",
    "summary": "Newsletter signup and Stripe checkout for orvain.dev.",
    "description": "The HTTP endpoints behind orvain.dev: joining the Orvain Dispatch newsletter, opening a Stripe Checkout page for an Orvain UI plan, confirming a paid order, cancelling a yearly plan and fetching the Figma file for a paid order. Every request and response body is JSON. Every error is JSON with a human `error` message, a machine `code`, the HTTP `status`, an optional `hint` and a `docs` link. There is no authentication header: purchases are tied to an email address and proven with the Stripe Checkout Session id.",
    "contact": {
      "name": "Orvain UI",
      "email": "hello@contactoussema.uk",
      "url": "https://orvain.dev/contact"
    },
    "termsOfService": "https://orvain.dev/terms"
  },
  "externalDocs": {
    "description": "API reference",
    "url": "https://orvain.dev/docs/api"
  },
  "servers": [
    {
      "url": "https://orvain.dev"
    }
  ],
  "tags": [
    {
      "name": "Newsletter",
      "description": "The Orvain Dispatch mailing list."
    },
    {
      "name": "Checkout",
      "description": "Buying, confirming and cancelling Orvain UI plans through Stripe."
    },
    {
      "name": "Delivery",
      "description": "Handing over what an order paid for."
    },
    {
      "name": "Webhooks",
      "description": "Called by Stripe, not by clients."
    },
    {
      "name": "Account",
      "description": "Signing in with an emailed link."
    }
  ],
  "paths": {
    "/api/subscribe": {
      "post": {
        "operationId": "subscribeToNewsletter",
        "tags": [
          "Newsletter"
        ],
        "summary": "Join the Orvain Dispatch newsletter",
        "description": "Adds an email address to the Orvain Dispatch mailing list (release notes and launch news). Idempotent: subscribing an address that is already on the list succeeds.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "An email address."
                  }
                },
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Subscribed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "The email address is missing or malformed. Codes: `invalid_email`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The endpoint was called with a verb other than POST. Codes: `method_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The mailing list provider failed. Codes: `upstream_error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Signups are not open on this deployment. Codes: `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/stripe/checkout": {
      "post": {
        "operationId": "createCheckoutSession",
        "tags": [
          "Checkout"
        ],
        "summary": "Open a Stripe Checkout page for a plan",
        "description": "Returns the URL of a hosted Stripe Checkout page for one Orvain UI plan. The price, billing mode and amount are decided server-side from product, tier and billing. Without an email only the Figma file can be bought, as a guest; the code library and the bundle need the signed-in account's email. Send the buyer to the returned URL to pay.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "product",
                  "tier"
                ],
                "properties": {
                  "product": {
                    "type": "string",
                    "enum": [
                      "library",
                      "figma",
                      "bundle"
                    ],
                    "description": "library = React and Vue code with the MCP server; figma = the Figma file; bundle = both."
                  },
                  "tier": {
                    "type": "string",
                    "enum": [
                      "personal",
                      "team",
                      "business"
                    ],
                    "description": "Licence size: personal is Solo (1 seat), team is Studio, business is Business."
                  },
                  "billing": {
                    "type": "string",
                    "enum": [
                      "yearly",
                      "lifetime"
                    ],
                    "description": "Payment term. A term the product is not sold on falls back to the product's own."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "The signed-in account's email. Omit it to buy as a guest; Stripe then collects the email."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A Checkout page was created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "url"
                  ],
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The Stripe Checkout page to send the buyer to."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The plan or email is invalid. Codes: `unknown_plan`, `invalid_email`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The endpoint was called with a verb other than POST. Codes: `method_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The plan exists but is not on sale online yet. Codes: `plan_unavailable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Stripe could not be reached or failed. Codes: `upstream_error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The payment provider is not set up on this deployment. Codes: `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/stripe/confirm": {
      "post": {
        "operationId": "confirmCheckout",
        "tags": [
          "Checkout"
        ],
        "summary": "Confirm a paid Checkout Session",
        "description": "Checks with Stripe that a Checkout Session was paid, by the given email, for an Orvain UI plan, and returns the plan it bought. For code library and bundle orders it also returns the licence key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sessionId",
                  "email"
                ],
                "properties": {
                  "sessionId": {
                    "type": "string",
                    "pattern": "^cs_",
                    "description": "A Stripe Checkout Session id (the order number on the receipt)."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "The email the payment was made with."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The payment is confirmed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "offer",
                    "sessionId"
                  ],
                  "properties": {
                    "offer": {
                      "$ref": "#/components/schemas/Offer"
                    },
                    "sessionId": {
                      "type": "string",
                      "pattern": "^cs_",
                      "description": "A Stripe Checkout Session id (the order number on the receipt)."
                    },
                    "subscriptionId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The Stripe subscription id for a yearly plan, null for a one-time purchase."
                    },
                    "licenceKey": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The code library licence key, or null for a Figma-only order or while it is still being issued (it is emailed once ready)."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The session id or email is missing. Codes: `invalid_request`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The payment was made with a different email. Codes: `email_mismatch`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Stripe has no such session. Codes: `not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The endpoint was called with a verb other than POST. Codes: `method_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The payment has not gone through yet. Codes: `payment_pending`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The payment is not for an Orvain UI plan. Codes: `unknown_offer`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Stripe could not be reached or failed. Codes: `upstream_error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The payment provider is not set up on this deployment. Codes: `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/stripe/cancel": {
      "post": {
        "operationId": "cancelSubscription",
        "tags": [
          "Checkout"
        ],
        "summary": "Cancel a yearly plan at the end of its term",
        "description": "Stops a yearly Orvain UI subscription from renewing. Access continues until the paid year ends; the response says when that is. Calling it again is safe and returns the same date.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "subscriptionId",
                  "email"
                ],
                "properties": {
                  "subscriptionId": {
                    "type": "string",
                    "pattern": "^sub_",
                    "description": "The Stripe subscription id."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "The email the subscription belongs to."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The subscription will not renew.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "endsAt"
                  ],
                  "properties": {
                    "endsAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "When access ends, or null if the subscription had already ended."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The subscription id or email is missing. Codes: `invalid_request`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The subscription belongs to a different email. Codes: `email_mismatch`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Stripe has no such subscription. Codes: `not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The endpoint was called with a verb other than POST. Codes: `method_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Stripe could not be reached or failed. Codes: `upstream_error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The payment provider is not set up on this deployment. Codes: `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/stripe/licence": {
      "post": {
        "operationId": "getLicenceDelivery",
        "tags": [
          "Delivery"
        ],
        "summary": "Get the licence key for a paid order",
        "description": "Returns the code library licence key for a Checkout Session that Stripe reports as paid for the Library or the bundle, issuing it if needed. The session id is the proof of purchase. key is null while the key is still being issued; it is emailed either way.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sessionId"
                ],
                "properties": {
                  "sessionId": {
                    "type": "string",
                    "pattern": "^cs_",
                    "description": "A Stripe Checkout Session id (the order number on the receipt)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The order is paid; here is the key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "offer",
                    "key",
                    "expiry",
                    "paidAt"
                  ],
                  "properties": {
                    "offer": {
                      "type": "object",
                      "description": "The plan that was paid for: product, tier and billing."
                    },
                    "key": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The licence key, or null while it is being issued."
                    },
                    "expiry": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "When a yearly licence lapses unless renewed; null for one-time."
                    },
                    "email": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "email",
                      "description": "The email the order was paid with."
                    },
                    "paidAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the order was placed."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The order number is malformed. Codes: `invalid_request`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Stripe has no such order. Codes: `not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The endpoint was called with a verb other than POST. Codes: `method_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The payment has not gone through yet. Codes: `payment_pending`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The order does not come with a licence key. Codes: `wrong_product`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Stripe could not be reached or failed. Codes: `upstream_error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The payment provider is not set up on this deployment. Codes: `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/stripe/figma": {
      "post": {
        "operationId": "getFigmaDelivery",
        "tags": [
          "Delivery"
        ],
        "summary": "Get the Figma file for a paid order",
        "description": "Returns the link to the Orvain UI Figma library for a Checkout Session that Stripe reports as paid for the Figma file or the bundle. The session id is the proof of purchase.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sessionId"
                ],
                "properties": {
                  "sessionId": {
                    "type": "string",
                    "pattern": "^cs_",
                    "description": "A Stripe Checkout Session id (the order number on the receipt)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The order is paid; here is the file.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "link",
                    "duplicateLink",
                    "paidAt"
                  ],
                  "properties": {
                    "link": {
                      "type": "string",
                      "format": "uri",
                      "description": "The Figma file."
                    },
                    "duplicateLink": {
                      "type": "string",
                      "format": "uri",
                      "description": "A link that duplicates the file into the buyer's Figma drafts."
                    },
                    "offer": {
                      "type": "object",
                      "description": "The plan that was paid for: product, tier and billing."
                    },
                    "email": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "email",
                      "description": "The email the order was paid with."
                    },
                    "paidAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the order was placed."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The order number is malformed. Codes: `invalid_request`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Stripe has no such order. Codes: `not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The endpoint was called with a verb other than POST. Codes: `method_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The payment has not gone through yet. Codes: `payment_pending`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The order is not for the Figma file or the bundle. Codes: `wrong_product`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Stripe could not be reached or failed. Codes: `upstream_error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The payment provider is not set up on this deployment. Codes: `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/request": {
      "post": {
        "operationId": "requestSignInLink",
        "tags": [
          "Account"
        ],
        "summary": "Email a sign-in link",
        "description": "Emails a link that signs the address in to orvain.dev. The link works for 15 minutes. The answer is the same whether or not the address has bought anything. Limited to 5 requests per 15 minutes per email and per IP.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "An email address."
                  },
                  "offer": {
                    "$ref": "#/components/schemas/Offer",
                    "description": "Optional: the plan the visitor wants to buy next. The link carries it on to checkout; it is never granted by signing in."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The email is on its way.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "expiresInSeconds"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "expiresInSeconds": {
                      "type": "integer",
                      "const": 900
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The email address is missing or malformed. Codes: `invalid_email`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The endpoint was called with a verb other than POST. Codes: `method_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many links asked for; see the Retry-After header. Codes: `rate_limited`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The email could not be sent. Codes: `upstream_error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Sign-in is not set up on this deployment. Codes: `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/verify": {
      "post": {
        "operationId": "verifySignInLink",
        "tags": [
          "Account"
        ],
        "summary": "Open a sign-in link",
        "description": "Checks a sign-in link's token and answers with the account it opens: the email and the best plan it still holds (from its licences, or its Figma order), with the licence key. An account with no valid plan comes back with `offer: null`, the free tier.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "The `token` parameter of the emailed link."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The link is valid.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "email",
                    "offer",
                    "payment",
                    "since"
                  ],
                  "properties": {
                    "email": {
                      "type": "string",
                      "format": "email",
                      "maxLength": 254,
                      "description": "An email address."
                    },
                    "offer": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/Offer"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "payment": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "sessionId": {
                          "type": "string",
                          "pattern": "^cs_",
                          "description": "A Stripe Checkout Session id (the order number on the receipt)."
                        },
                        "subscriptionId": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "endsAt": {
                          "type": "string",
                          "format": "date-time",
                          "description": "Set when a yearly plan's renewal was cancelled."
                        },
                        "licenceKey": {
                          "type": "string",
                          "description": "The code library licence key."
                        },
                        "expiry": {
                          "type": "string",
                          "format": "date-time",
                          "description": "The licence's expiry, for a yearly plan."
                        }
                      }
                    },
                    "since": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "When the plan was bought."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The link is malformed, tampered with or older than 15 minutes. Codes: `invalid_token`, `expired_token`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The endpoint was called with a verb other than POST. Codes: `method_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The licence server or Stripe could not be reached. Codes: `upstream_error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Sign-in is not set up on this deployment. Codes: `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/stripe/webhook": {
      "post": {
        "operationId": "receiveStripeWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Stripe event receiver (Stripe only)",
        "description": "Receives checkout.session.completed, checkout.session.async_payment_succeeded and invoice.paid events from Stripe to email orders, issue licences and extend renewals. Requests must carry a valid Stripe-Signature header; anything else is rejected. Not for clients.",
        "parameters": [
          {
            "name": "Stripe-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stripe's HMAC-SHA256 signature of the request, with its timestamp."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "A Stripe Event object.",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Handled or deliberately ignored."
          },
          "400": {
            "description": "The body is not JSON. Codes: `invalid_json`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The signature is missing, stale or wrong. Codes: `bad_signature`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The endpoint was called with a verb other than POST. Codes: `method_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Processing failed; Stripe retries. Codes: `retry`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Webhooks are not configured on this deployment. Codes: `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Ok": {
        "type": "object",
        "required": [
          "ok"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "Offer": {
        "type": "object",
        "description": "What an order bought.",
        "required": [
          "product",
          "tier",
          "billing"
        ],
        "properties": {
          "product": {
            "type": "string",
            "enum": [
              "library",
              "figma",
              "bundle"
            ]
          },
          "tier": {
            "type": "string",
            "enum": [
              "personal",
              "team",
              "business"
            ]
          },
          "billing": {
            "type": "string",
            "enum": [
              "yearly",
              "lifetime"
            ]
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Every error the API returns, at every status.",
        "required": [
          "error",
          "code",
          "status",
          "docs"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "What went wrong, in a sentence a person can read."
          },
          "code": {
            "type": "string",
            "description": "A stable machine-readable code to branch on.",
            "examples": [
              "invalid_email",
              "not_found",
              "method_not_allowed"
            ]
          },
          "status": {
            "type": "integer",
            "description": "The HTTP status, repeated."
          },
          "hint": {
            "type": "string",
            "description": "How to fix the request, when there is something to fix."
          },
          "docs": {
            "type": "string",
            "format": "uri",
            "description": "Where the error codes are documented."
          }
        }
      }
    }
  }
}