{
  "openapi": "3.0.1",
  "info": {
    "title": "Netavo API: Platform",
    "description": "Account-wide features: branding, webhooks, events and API keys.",
    "contact": {
      "name": "Netavo Support",
      "email": "support@netavo.com"
    },
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://partners.netavo.com/api"
    }
  ],
  "paths": {
    "/v1/platform/api-keys": {
      "get": {
        "tags": [
          "ApiKeys"
        ],
        "summary": "List API keys",
        "description": "Every API key of your organisation, newest first, active or not. Secrets are never\nreturned.",
        "operationId": "listApiKeys",
        "responses": {
          "200": {
            "description": "The keys.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyList"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/api-keys",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "API keys are managed with an API key that has explicit scopes.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "insufficient-scope": {
                    "summary": "Insufficient scope",
                    "value": {
                      "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                      "title": "Insufficient scope",
                      "status": 403,
                      "detail": "API keys are managed with an API key. Signed-in users manage them in the portal.",
                      "instance": "/api/v1/platform/api-keys",
                      "code": "insufficient-scope",
                      "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/api-keys",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.keys:read"
      },
      "post": {
        "tags": [
          "ApiKeys"
        ],
        "summary": "Create an API key",
        "description": "Creates a key for your organisation. It can have only scopes the calling key holds, cannot\noutlive it, and inherits its address limits (which it may narrow). The secret is in this\nresponse only — store it now.",
        "operationId": "createApiKey",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Any unique string (a UUID is ideal). Retrying with the same key returns the first result instead of acting twice. Keys are kept for 24 hours; while the first request is still running a retry gets 409 `idempotency-key-in-use`.",
            "schema": {
              "maxLength": 255,
              "type": "string"
            },
            "example": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14"
          }
        ],
        "requestBody": {
          "description": "The key to create.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiKeyCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The key and its secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreated"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/validation-failed",
                  "title": "Validation failed",
                  "status": 400,
                  "detail": "limit must be between 1 and 100.",
                  "instance": "/api/v1/platform/api-keys",
                  "code": "validation-failed",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/api-keys",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key would have more access than the key creating it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "insufficient-scope": {
                    "summary": "Insufficient scope",
                    "value": {
                      "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                      "title": "Insufficient scope",
                      "status": 403,
                      "detail": "A key cannot have scopes the key creating it does not: iot.sims:cease.",
                      "instance": "/api/v1/platform/api-keys",
                      "code": "insufficient-scope",
                      "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "The `Idempotency-Key` was already used for a different request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/idempotency-key-reused",
                  "title": "Idempotency key reused",
                  "status": 422,
                  "detail": "This Idempotency-Key was used with a different request body.",
                  "instance": "/api/v1/platform/api-keys",
                  "code": "idempotency-key-reused",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/api-keys",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.keys:manage"
      }
    },
    "/v1/platform/api-keys/{keyId}": {
      "delete": {
        "tags": [
          "ApiKeys"
        ],
        "summary": "Revoke an API key",
        "description": "Stops the key working at once. Revoking a key that is already revoked succeeds.",
        "operationId": "revokeApiKey",
        "parameters": [
          {
            "name": "keyId",
            "in": "path",
            "description": "The key's id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "key_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f"
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/api-keys/{keyId}",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.keys:manage` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.keys:manage scope.",
                  "instance": "/api/v1/platform/api-keys/{keyId}",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or not in your account. Resources outside your organisation are always 404, never 403.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/not-found",
                  "title": "Not found",
                  "status": 404,
                  "detail": "No such resource in your account.",
                  "instance": "/api/v1/platform/api-keys/{keyId}",
                  "code": "not-found",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/api-keys/{keyId}",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.keys:manage"
      }
    },
    "/v1/platform/branding": {
      "get": {
        "tags": [
          "Branding"
        ],
        "summary": "Get branding information for the authenticated user or extension\nWalks up the organisation hierarchy to find custom branding",
        "operationId": "getBranding",
        "responses": {
          "200": {
            "description": "Branding information found and returned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrandingResponseApiResponse"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated or invalid token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.branding:read` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.branding:read scope.",
                  "instance": "/api/v1/platform/branding",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "404": {
            "description": "No custom branding found for this organisation hierarchy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/branding",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.branding:read"
      }
    },
    "/v1/platform/events": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "List events",
        "description": "Notifications raised for your account in the last 30 days, newest first — for partners,\nthose of your customer organisations too. Use it to catch up after downtime.",
        "operationId": "listEvents",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items to return, 1 to 100.",
            "schema": {
              "maximum": 100,
              "minimum": 1,
              "type": "integer",
              "format": "int32",
              "example": 50
            },
            "example": 50
          },
          {
            "name": "startingAfter",
            "in": "query",
            "description": "Cursor from the previous page's `nextCursor`. Leave out for the first page.",
            "schema": {
              "type": "string",
              "example": "c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg"
            },
            "example": "c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg"
          },
          {
            "name": "type",
            "in": "query",
            "description": "Only events of this type.",
            "schema": {
              "type": "string"
            },
            "example": "iot.sim.allowance_reached"
          },
          {
            "name": "since",
            "in": "query",
            "description": "Only events at or after this time.",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "example": "2026-09-24T00:00:00Z"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of events.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventList"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/validation-failed",
                  "title": "Validation failed",
                  "status": 400,
                  "detail": "limit must be between 1 and 100.",
                  "instance": "/api/v1/platform/events",
                  "code": "validation-failed",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/events",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.events:read` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.events:read scope.",
                  "instance": "/api/v1/platform/events",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/events",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.events:read"
      }
    },
    "/v1/platform/events/{eventId}": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "Get an event",
        "description": "One event by id, exactly as it was delivered.",
        "operationId": "getEvent",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "description": "The event's id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72"
          }
        ],
        "responses": {
          "200": {
            "description": "The event.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEvent"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/events/{eventId}",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.events:read` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.events:read scope.",
                  "instance": "/api/v1/platform/events/{eventId}",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or not in your account. Resources outside your organisation are always 404, never 403.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/not-found",
                  "title": "Not found",
                  "status": 404,
                  "detail": "No such resource in your account.",
                  "instance": "/api/v1/platform/events/{eventId}",
                  "code": "not-found",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/events/{eventId}",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.events:read"
      }
    },
    "/v1/platform/notification-types": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "List notification types",
        "description": "The notification catalogue, filtered to your account's service areas. `available: false`\nmarks a type that is catalogued but not yet sent; you can subscribe to it now.",
        "operationId": "listNotificationTypes",
        "parameters": [
          {
            "name": "serviceArea",
            "in": "query",
            "description": "Only this service area: `iot`, `connectivity`, `voice`, `hosting` or `platform`.",
            "schema": {
              "type": "string"
            },
            "example": "iot"
          }
        ],
        "responses": {
          "200": {
            "description": "The catalogue.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationTypeList"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/validation-failed",
                  "title": "Validation failed",
                  "status": 400,
                  "detail": "limit must be between 1 and 100.",
                  "instance": "/api/v1/platform/notification-types",
                  "code": "validation-failed",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/notification-types",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.events:read` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.events:read scope.",
                  "instance": "/api/v1/platform/notification-types",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/notification-types",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.events:read"
      }
    },
    "/v1/platform/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhooks",
        "description": "Every webhook endpoint on your account, newest first — for partners, those of your\ncustomer organisations too.",
        "operationId": "listWebhooks",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items to return, 1 to 100.",
            "schema": {
              "maximum": 100,
              "minimum": 1,
              "type": "integer",
              "format": "int32",
              "example": 50
            },
            "example": 50
          },
          {
            "name": "startingAfter",
            "in": "query",
            "description": "Cursor from the previous page's `nextCursor`. Leave out for the first page.",
            "schema": {
              "type": "string",
              "example": "c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg"
            },
            "example": "c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of webhooks.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookList"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/validation-failed",
                  "title": "Validation failed",
                  "status": 400,
                  "detail": "limit must be between 1 and 100.",
                  "instance": "/api/v1/platform/webhooks",
                  "code": "validation-failed",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/webhooks",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.webhooks:read` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.webhooks:read scope.",
                  "instance": "/api/v1/platform/webhooks",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/webhooks",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.webhooks:read"
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Create a webhook",
        "description": "Adds an endpoint. The response holds the signing secret, which is not shown again. The URL\nmust be HTTPS on a public address. Send a test event afterwards to check your endpoint.\nPartners can create one for a customer organisation with `organisationId`.",
        "operationId": "createWebhook",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Any unique string (a UUID is ideal). Retrying with the same key returns the first result instead of acting twice. Keys are kept for 24 hours; while the first request is still running a retry gets 409 `idempotency-key-in-use`.",
            "schema": {
              "maxLength": 255,
              "type": "string"
            },
            "example": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14"
          }
        ],
        "requestBody": {
          "description": "The endpoint to add.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The webhook and its signing secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookCreated"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/validation-failed",
                  "title": "Validation failed",
                  "status": 400,
                  "detail": "limit must be between 1 and 100.",
                  "instance": "/api/v1/platform/webhooks",
                  "code": "validation-failed",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/webhooks",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.webhooks:manage` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.webhooks:manage scope.",
                  "instance": "/api/v1/platform/webhooks",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "404": {
            "description": "`organisationId` is not an organisation in your account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/not-found",
                  "title": "Not found",
                  "status": 404,
                  "detail": "No organisation with that id in your account.",
                  "instance": "/api/v1/platform/webhooks",
                  "code": "not-found",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "409": {
            "description": "The organisation already has the most webhooks it may have (20).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/invalid-state",
                  "title": "Invalid state",
                  "status": 409,
                  "detail": "An organisation can have at most 20 webhooks.",
                  "instance": "/api/v1/platform/webhooks",
                  "code": "invalid-state",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "422": {
            "description": "The `Idempotency-Key` was already used for a different request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/idempotency-key-reused",
                  "title": "Idempotency key reused",
                  "status": 422,
                  "detail": "This Idempotency-Key was used with a different request body.",
                  "instance": "/api/v1/platform/webhooks",
                  "code": "idempotency-key-reused",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/webhooks",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.webhooks:manage"
      }
    },
    "/v1/platform/webhooks/{webhookId}": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Get a webhook",
        "description": "One webhook endpoint. The signing secret is never returned here.",
        "operationId": "getWebhook",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "The webhook's id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f"
          }
        ],
        "responses": {
          "200": {
            "description": "The webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.webhooks:read` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.webhooks:read scope.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or not in your account. Resources outside your organisation are always 404, never 403.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/not-found",
                  "title": "Not found",
                  "status": 404,
                  "detail": "No such resource in your account.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "not-found",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.webhooks:read"
      },
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Update a webhook",
        "description": "Changes the URL, the types it receives, or turns it on and off. Leave a field out to keep\nit. Re-enabling a disabled webhook resets its failure count.",
        "operationId": "updateWebhook",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "The webhook's id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f"
          }
        ],
        "requestBody": {
          "description": "The fields to change.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/validation-failed",
                  "title": "Validation failed",
                  "status": 400,
                  "detail": "limit must be between 1 and 100.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "validation-failed",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.webhooks:manage` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.webhooks:manage scope.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or not in your account. Resources outside your organisation are always 404, never 403.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/not-found",
                  "title": "Not found",
                  "status": 404,
                  "detail": "No such resource in your account.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "not-found",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.webhooks:manage"
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook",
        "description": "Removes the endpoint and its delivery log. Deliveries in flight are abandoned.",
        "operationId": "deleteWebhook",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "The webhook's id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.webhooks:manage` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.webhooks:manage scope.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or not in your account. Resources outside your organisation are always 404, never 403.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/not-found",
                  "title": "Not found",
                  "status": 404,
                  "detail": "No such resource in your account.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "not-found",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.webhooks:manage"
      }
    },
    "/v1/platform/webhooks/{webhookId}/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List deliveries",
        "description": "Delivery attempts to this endpoint in the last 30 days, newest first. Failed deliveries are\nretried with back-off for about 24 hours.",
        "operationId": "listWebhookDeliveries",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "The webhook's id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f"
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many items to return, 1 to 100.",
            "schema": {
              "maximum": 100,
              "minimum": 1,
              "type": "integer",
              "format": "int32",
              "example": 50
            },
            "example": 50
          },
          {
            "name": "startingAfter",
            "in": "query",
            "description": "Cursor from the previous page's `nextCursor`. Leave out for the first page.",
            "schema": {
              "type": "string",
              "example": "c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg"
            },
            "example": "c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg"
          },
          {
            "name": "outcome",
            "in": "query",
            "description": "Only attempts with this outcome: `succeeded`, `failed` or `pending`.",
            "schema": {
              "type": "string"
            },
            "example": "failed"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of deliveries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryList"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/validation-failed",
                  "title": "Validation failed",
                  "status": 400,
                  "detail": "limit must be between 1 and 100.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries",
                  "code": "validation-failed",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.webhooks:read` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.webhooks:read scope.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or not in your account. Resources outside your organisation are always 404, never 403.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/not-found",
                  "title": "Not found",
                  "status": 404,
                  "detail": "No such resource in your account.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries",
                  "code": "not-found",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.webhooks:read"
      }
    },
    "/v1/platform/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Redeliver an event",
        "description": "Sends the event of a delivery attempt to the webhook again, now, whatever its outcome was.\nRefused while the webhook is switched off or the delivery already has a retry pending.",
        "operationId": "redeliverWebhookDelivery",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "The webhook's id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f"
          },
          {
            "name": "deliveryId",
            "in": "path",
            "description": "A delivery attempt's id, from the delivery log.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "dlv_0192a4c3e5f67b8d9e0f1a2b3c4d5e71"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Any unique string (a UUID is ideal). Retrying with the same key returns the first result instead of acting twice. Keys are kept for 24 hours; while the first request is still running a retry gets 409 `idempotency-key-in-use`.",
            "schema": {
              "maxLength": 255,
              "type": "string"
            },
            "example": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14"
          }
        ],
        "responses": {
          "202": {
            "description": "Queued. The new attempt appears in the delivery log.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDelivery"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.webhooks:manage` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.webhooks:manage scope.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or not in your account. Resources outside your organisation are always 404, never 403.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/not-found",
                  "title": "Not found",
                  "status": 404,
                  "detail": "No such resource in your account.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver",
                  "code": "not-found",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "409": {
            "description": "The webhook is switched off, or the delivery is already queued.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/invalid-state",
                  "title": "Invalid state",
                  "status": 409,
                  "detail": "The webhook is switched off. Enable it before redelivering.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver",
                  "code": "invalid-state",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "422": {
            "description": "The `Idempotency-Key` was already used for a different request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/idempotency-key-reused",
                  "title": "Idempotency key reused",
                  "status": 422,
                  "detail": "This Idempotency-Key was used with a different request body.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver",
                  "code": "idempotency-key-reused",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "503": {
            "description": "Deliveries cannot be queued right now. Retry shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/service-unavailable",
                  "title": "Service unavailable",
                  "status": 503,
                  "detail": "Deliveries cannot be queued right now.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver",
                  "code": "service-unavailable",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.webhooks:manage"
      }
    },
    "/v1/platform/webhooks/{webhookId}/rotate-secret": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Rotate a webhook's signing secret",
        "description": "Issues a new secret. For 24 hours events carry signatures from both secrets, so you can\ndeploy the new one without dropping events.",
        "operationId": "rotateWebhookSecret",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "The webhook's id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Any unique string (a UUID is ideal). Retrying with the same key returns the first result instead of acting twice. Keys are kept for 24 hours; while the first request is still running a retry gets 409 `idempotency-key-in-use`.",
            "schema": {
              "maxLength": 255,
              "type": "string"
            },
            "example": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14"
          }
        ],
        "responses": {
          "200": {
            "description": "The new secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecretRotation"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/rotate-secret",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.webhooks:manage` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.webhooks:manage scope.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/rotate-secret",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or not in your account. Resources outside your organisation are always 404, never 403.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/not-found",
                  "title": "Not found",
                  "status": 404,
                  "detail": "No such resource in your account.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/rotate-secret",
                  "code": "not-found",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "422": {
            "description": "The `Idempotency-Key` was already used for a different request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/idempotency-key-reused",
                  "title": "Idempotency key reused",
                  "status": 422,
                  "detail": "This Idempotency-Key was used with a different request body.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/rotate-secret",
                  "code": "idempotency-key-reused",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/rotate-secret",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.webhooks:manage"
      }
    },
    "/v1/platform/webhooks/{webhookId}/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a test event",
        "description": "Queues a `platform.webhook.test` event to this endpoint only. Check the delivery log for the\nresult.",
        "operationId": "sendTestEvent",
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "The webhook's id.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Any unique string (a UUID is ideal). Retrying with the same key returns the first result instead of acting twice. Keys are kept for 24 hours; while the first request is still running a retry gets 409 `idempotency-key-in-use`.",
            "schema": {
              "maxLength": 255,
              "type": "string"
            },
            "example": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14"
          }
        ],
        "responses": {
          "202": {
            "description": "Queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestEventResult"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, expired or revoked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/unauthorized",
                  "title": "Unauthorized",
                  "status": 401,
                  "detail": "The bearer token is missing, expired or revoked.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/test",
                  "code": "unauthorized",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the `platform.webhooks:manage` scope.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/insufficient-scope",
                  "title": "Insufficient scope",
                  "status": 403,
                  "detail": "This key needs the platform.webhooks:manage scope.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/test",
                  "code": "insufficient-scope",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or not in your account. Resources outside your organisation are always 404, never 403.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/not-found",
                  "title": "Not found",
                  "status": 404,
                  "detail": "No such resource in your account.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/test",
                  "code": "not-found",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "422": {
            "description": "The `Idempotency-Key` was already used for a different request.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/idempotency-key-reused",
                  "title": "Idempotency key reused",
                  "status": 422,
                  "detail": "This Idempotency-Key was used with a different request body.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/test",
                  "code": "idempotency-key-reused",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "format": "int32"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/rate-limited",
                  "title": "Too many requests",
                  "status": 429,
                  "detail": "Rate limit exceeded; retry after 12 seconds.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/test",
                  "code": "rate-limited",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          },
          "503": {
            "description": "The event could not be queued. Retry shortly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "https://partners.netavo.com/api/problems/service-unavailable",
                  "title": "Service unavailable",
                  "status": 503,
                  "detail": "The test event could not be queued. Try again shortly.",
                  "instance": "/api/v1/platform/webhooks/{webhookId}/test",
                  "code": "service-unavailable",
                  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          }
        ],
        "x-required-scope": "platform.webhooks:manage"
      }
    }
  },
  "components": {
    "schemas": {
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The key's id, `key_…`.",
            "example": "key_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f"
          },
          "name": {
            "type": "string",
            "description": "What the key is for.",
            "example": "Fleet dashboard"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The scopes the key may use.",
            "example": [
              "iot.sims:read",
              "iot.usage:read"
            ]
          },
          "status": {
            "type": "string",
            "description": "`active`, `expired` or `revoked`.",
            "example": "active"
          },
          "allowedIpRanges": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Source addresses and CIDR ranges the key is limited to; empty for anywhere.",
            "example": [
              "203.0.113.0/24"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-09-25T10:00:00Z"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "example": "2027-09-25T10:00:00Z"
          },
          "lastUsedAt": {
            "type": "string",
            "description": "When the key was last used, to the nearest few minutes; null if never.",
            "format": "date-time",
            "nullable": true,
            "example": "2026-09-25T11:30:00Z"
          },
          "revokedAt": {
            "type": "string",
            "description": "When the key was revoked; null unless it has been.",
            "format": "date-time",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "An API key. The secret is never returned after creation."
      },
      "ApiKeyCreate": {
        "required": [
          "name",
          "scopes"
        ],
        "type": "object",
        "properties": {
          "name": {
            "maxLength": 100,
            "minLength": 1,
            "type": "string",
            "description": "What the key is for, so you can recognise it later.",
            "example": "Fleet dashboard"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The scopes the key may use. Each must be one the calling credential holds itself: a key\ncan never do more than the key that created it.",
            "example": [
              "iot.sims:read",
              "iot.usage:read"
            ]
          },
          "expiresAt": {
            "type": "string",
            "description": "When the key stops working, at most a year ahead (and no later than the calling key's own\nexpiry). Leave out for a year.",
            "format": "date-time",
            "nullable": true,
            "example": "2027-03-31T00:00:00Z"
          },
          "allowedIpRanges": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Limit the key to these source addresses or CIDR ranges. Leave out for anywhere — unless the\ncalling key is itself limited, in which case the new key inherits its limits and may only\nnarrow them.",
            "nullable": true,
            "example": [
              "203.0.113.0/24"
            ]
          }
        },
        "additionalProperties": false,
        "description": "A new API key."
      },
      "ApiKeyCreated": {
        "type": "object",
        "properties": {
          "apiKey": {
            "$ref": "#/components/schemas/ApiKey"
          },
          "secret": {
            "type": "string",
            "description": "The key itself — send it as `Authorization: Bearer …`. Shown only in this response: store\nit now; it cannot be read back.",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.…"
          }
        },
        "additionalProperties": false,
        "description": "A created key and its secret."
      },
      "ApiKeyList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKey"
            },
            "description": "Newest first."
          }
        },
        "additionalProperties": false,
        "description": "The organisation's API keys."
      },
      "BrandingResponse": {
        "type": "object",
        "properties": {
          "organisationId": {
            "type": "string",
            "description": "Organisation ID that owns this branding",
            "format": "uuid"
          },
          "organisationName": {
            "type": "string",
            "description": "Organisation name to display in the application",
            "nullable": true
          },
          "primaryColor": {
            "type": "string",
            "description": "Primary brand color (hex format, e.g., #5A66F1)",
            "nullable": true
          },
          "secondaryColor": {
            "type": "string",
            "description": "Secondary brand color (hex format, e.g., #60A5FA)",
            "nullable": true
          },
          "supportPhone": {
            "type": "string",
            "description": "Support contact phone number",
            "nullable": true
          },
          "supportEmail": {
            "type": "string",
            "description": "Support contact email address",
            "nullable": true
          },
          "logoSvg": {
            "type": "string",
            "description": "SVG logo content inline (full SVG markup)",
            "nullable": true
          },
          "isInherited": {
            "type": "boolean",
            "description": "Indicates if this branding is inherited from a parent organisation"
          }
        },
        "additionalProperties": false,
        "description": "API response model for organisation branding information\nIncludes inline SVG logo content for authenticated users and extensions"
      },
      "BrandingResponseApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "$ref": "#/components/schemas/BrandingResponse"
          },
          "message": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Standard API response envelope for success responses"
      },
      "ErrorDetail": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "details": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "error": {
            "$ref": "#/components/schemas/ErrorDetail"
          }
        },
        "additionalProperties": false,
        "description": "Standard API response for error cases"
      },
      "EventList": {
        "required": [
          "data",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            },
            "description": "This page of results."
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether another page follows.",
            "example": false
          },
          "nextCursor": {
            "type": "string",
            "description": "Pass as `startingAfter` to get the next page. Null on the last page.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "A page of results. Follow `nextCursor` for the next one."
      },
      "NotificationChannel": {
        "enum": [
          "webhook",
          "email",
          "portal"
        ],
        "type": "string",
        "description": "Ways a notification can reach a customer."
      },
      "NotificationType": {
        "required": [
          "available",
          "channels",
          "description",
          "examplePayload",
          "optIn",
          "serviceArea",
          "type"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "iot.sim.allowance_reached"
          },
          "serviceArea": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceArea"
              }
            ],
            "example": "iot"
          },
          "description": {
            "type": "string",
            "example": "A SIM used its whole data allowance for the period and has stopped passing data."
          },
          "optIn": {
            "type": "boolean",
            "description": "High-volume types are only sent to webhooks that name them in `types`.",
            "example": false
          },
          "available": {
            "type": "boolean",
            "description": "False for a type that is catalogued (so it can be subscribed to and documented) but not yet\nraised by anything. Subscribing to it is allowed; nothing arrives until it is.",
            "example": true
          },
          "channels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NotificationChannel"
            },
            "example": [
              "webhook"
            ]
          },
          "examplePayload": {
            "type": "object",
            "description": "An example of the type's `data` object."
          }
        },
        "additionalProperties": false,
        "description": "One entry in the notification catalogue."
      },
      "NotificationTypeList": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NotificationType"
            }
          }
        },
        "additionalProperties": false,
        "description": "The notification catalogue, filtered to the service areas your account has."
      },
      "Problem": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "Identifies the kind of problem.",
            "example": "https://partners.netavo.com/api/problems/not-found"
          },
          "title": {
            "type": "string",
            "description": "Short, human-readable summary of the problem type.",
            "example": "Not found"
          },
          "status": {
            "type": "integer",
            "description": "The HTTP status code.",
            "format": "int32",
            "example": 404
          },
          "detail": {
            "type": "string",
            "description": "What went wrong with this request.",
            "nullable": true,
            "example": "No SIM with ICCID 8944110068212345678 in your account."
          },
          "instance": {
            "type": "string",
            "description": "The request path.",
            "nullable": true,
            "example": "/api/v1/iot/sims/8944110068212345678"
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable code, the last segment of `type`.",
            "example": "not-found"
          },
          "traceId": {
            "type": "string",
            "description": "Quote this if you contact support.",
            "nullable": true,
            "example": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "description": "Per-field messages when `code` is `validation-failed`.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "An RFC 9457 problem document. `type` and `code` are stable; `title` and\n`detail` are for people."
      },
      "SecretRotation": {
        "required": [
          "previousSecretExpiresAt",
          "signingSecret"
        ],
        "type": "object",
        "properties": {
          "signingSecret": {
            "type": "string",
            "example": "whsec_9pD2kR5tV8xA1cF4hJ7mN0qS3uW6yZ9b"
          },
          "previousSecretExpiresAt": {
            "type": "string",
            "description": "Events are signed with both secrets until then.",
            "format": "date-time",
            "example": "2026-09-26T09:30:00Z"
          }
        },
        "additionalProperties": false,
        "description": "A new signing secret. Events are signed with both secrets until `previousSecretExpiresAt`."
      },
      "ServiceArea": {
        "enum": [
          "voice",
          "connectivity",
          "hosting",
          "iot",
          "platform"
        ],
        "type": "string",
        "description": "The product area a notification belongs to; `platform` is the account itself."
      },
      "TestEventResult": {
        "required": [
          "eventId"
        ],
        "type": "object",
        "properties": {
          "eventId": {
            "type": "string",
            "description": "The event's id; find its delivery in the webhook's delivery log.",
            "example": "evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e70"
          }
        },
        "additionalProperties": false,
        "description": "The test event that was queued."
      },
      "Webhook": {
        "required": [
          "createdAt",
          "enabled",
          "id",
          "includeSubtree",
          "organisationId",
          "serviceAreas",
          "status",
          "types",
          "updatedAt",
          "url"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The webhook's id, `wh_…`.",
            "example": "wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f"
          },
          "organisationId": {
            "type": "string",
            "description": "The organisation that owns the webhook.",
            "format": "uuid",
            "example": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90"
          },
          "url": {
            "type": "string",
            "description": "Where we POST events. HTTPS on a public address; redirects are not followed.",
            "example": "https://example.com/hooks/thingsim"
          },
          "description": {
            "type": "string",
            "nullable": true,
            "example": "Ops alerts"
          },
          "types": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Notification types to send. Empty with empty `serviceAreas` means every type your account\ncan receive, except opt-in types.",
            "example": [
              "iot.sim.allowance_reached",
              "iot.sim.usage_threshold"
            ]
          },
          "serviceAreas": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceArea"
            },
            "description": "Send every type in these service areas."
          },
          "includeSubtree": {
            "type": "boolean",
            "description": "Partners: also send events for customer organisations below you.",
            "example": false
          },
          "enabled": {
            "type": "boolean",
            "example": true
          },
          "status": {
            "type": "string",
            "description": "`active`, `failing` while deliveries are being retried, or `disabled` (switched off, or\nafter a day of failures).",
            "example": "active"
          },
          "disabledReason": {
            "type": "string",
            "description": "Why a disabled webhook was switched off.",
            "nullable": true,
            "example": null
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-09-20T10:00:00Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-09-20T10:00:00Z"
          }
        },
        "additionalProperties": false,
        "description": "A webhook endpoint."
      },
      "WebhookCreate": {
        "required": [
          "url"
        ],
        "type": "object",
        "properties": {
          "url": {
            "minLength": 1,
            "type": "string",
            "description": "Where we POST events. HTTPS on a public address; redirects are not followed.",
            "example": "https://example.com/hooks/thingsim"
          },
          "description": {
            "type": "string",
            "nullable": true,
            "example": "Ops alerts"
          },
          "types": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Notification types to send. Leave out (with `serviceAreas`) for every type your account can\nreceive, except opt-in types.",
            "nullable": true,
            "example": [
              "iot.sim.allowance_reached",
              "iot.sim.usage_threshold"
            ]
          },
          "serviceAreas": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceArea"
            },
            "description": "Send every type in these service areas.",
            "nullable": true
          },
          "includeSubtree": {
            "type": "boolean",
            "description": "Partners: also send events for customer organisations below you. Null or left out is false.",
            "nullable": true,
            "example": false
          },
          "organisationId": {
            "type": "string",
            "description": "Partners: create the webhook for a customer organisation below you. Defaults to your own\norganisation.",
            "format": "uuid",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "A new webhook endpoint."
      },
      "WebhookCreated": {
        "required": [
          "signingSecret",
          "webhook"
        ],
        "type": "object",
        "properties": {
          "webhook": {
            "$ref": "#/components/schemas/Webhook"
          },
          "signingSecret": {
            "type": "string",
            "description": "Verifies the `Webhook-Signature` header.",
            "example": "whsec_3yJ8mQ2vN6pR9tW1zC4fH7kL0sX5bE8g"
          }
        },
        "additionalProperties": false,
        "description": "The new webhook and its signing secret. The secret is only ever returned here and by\nrotate-secret: store it now."
      },
      "WebhookDelivery": {
        "required": [
          "attempt",
          "attemptedAt",
          "durationMs",
          "eventId",
          "eventType",
          "id",
          "outcome"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "This attempt, `dlv_…`. Pass it to redeliver the event.",
            "example": "dlv_0192a4c3e5f67b8d9e0f1a2b3c4d5e71"
          },
          "eventId": {
            "type": "string",
            "example": "evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72"
          },
          "eventType": {
            "type": "string",
            "example": "iot.sim.allowance_reached"
          },
          "attempt": {
            "type": "integer",
            "format": "int32",
            "example": 1
          },
          "outcome": {
            "type": "string",
            "description": "`succeeded`, `failed` or `pending` (queued, not yet attempted).",
            "example": "succeeded"
          },
          "responseStatus": {
            "type": "integer",
            "description": "Your endpoint's HTTP status; null if it could not be reached.",
            "format": "int32",
            "nullable": true,
            "example": 200
          },
          "durationMs": {
            "type": "integer",
            "format": "int32",
            "example": 184
          },
          "error": {
            "type": "string",
            "nullable": true,
            "example": null
          },
          "attemptedAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-09-25T09:12:45Z"
          },
          "nextAttemptAt": {
            "type": "string",
            "description": "When a failed delivery will be retried.",
            "format": "date-time",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "One attempt to deliver an event to a webhook."
      },
      "WebhookDeliveryList": {
        "required": [
          "data",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDelivery"
            },
            "description": "This page of results."
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether another page follows.",
            "example": false
          },
          "nextCursor": {
            "type": "string",
            "description": "Pass as `startingAfter` to get the next page. Null on the last page.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "A page of results. Follow `nextCursor` for the next one."
      },
      "WebhookEvent": {
        "required": [
          "data",
          "id",
          "occurredAt",
          "organisationId",
          "serviceArea",
          "type"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "`evt_…`. Unique per event; deliveries can repeat, so de-duplicate on it.",
            "example": "evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72"
          },
          "type": {
            "type": "string",
            "description": "The notification type.",
            "example": "iot.sim.allowance_reached"
          },
          "serviceArea": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ServiceArea"
              }
            ],
            "example": "iot"
          },
          "organisationId": {
            "type": "string",
            "description": "The organisation the event is about.",
            "format": "uuid",
            "example": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90"
          },
          "occurredAt": {
            "type": "string",
            "description": "When the change took effect.",
            "format": "date-time",
            "example": "2026-09-25T09:12:44Z"
          },
          "data": {
            "type": "object",
            "description": "The type's payload; see the notification catalogue.",
            "example": {
              "iccid": "8944110068212345678",
              "label": "Car park barrier 3",
              "planId": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
              "allowanceBytes": 1073741824,
              "usedBytes": 1073741824,
              "periodEnd": "2026-10-01T00:00:00Z"
            }
          }
        },
        "additionalProperties": false,
        "description": "The body of every webhook POST, and an item in the event log. `data` depends on `type`: see the\nnotification catalogue."
      },
      "WebhookList": {
        "required": [
          "data",
          "hasMore"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            },
            "description": "This page of results."
          },
          "hasMore": {
            "type": "boolean",
            "description": "Whether another page follows.",
            "example": false
          },
          "nextCursor": {
            "type": "string",
            "description": "Pass as `startingAfter` to get the next page. Null on the last page.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "A page of results. Follow `nextCursor` for the next one."
      },
      "WebhookUpdate": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Where we POST events. HTTPS on a public address; redirects are not followed.",
            "nullable": true,
            "example": "https://example.com/hooks/thingsim"
          },
          "description": {
            "type": "string",
            "nullable": true,
            "example": "Ops alerts"
          },
          "types": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Replaces the types sent.",
            "nullable": true,
            "example": [
              "iot.sim.allowance_reached"
            ]
          },
          "serviceAreas": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceArea"
            },
            "description": "Replaces the service areas sent.",
            "nullable": true
          },
          "includeSubtree": {
            "type": "boolean",
            "nullable": true,
            "example": false
          },
          "enabled": {
            "type": "boolean",
            "description": "Re-enabling a disabled webhook resets its failure count.",
            "nullable": true,
            "example": true
          }
        },
        "additionalProperties": false,
        "description": "Fields to change. Leave a field out to keep it."
      }
    },
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "description": "An API key from the portal, sent as `Authorization: Bearer <key>`. OAuth 2.1 client-credentials access tokens are accepted the same way.",
        "scheme": "bearer",
        "bearerFormat": "API key"
      }
    }
  },
  "tags": [
    {
      "name": "ApiKeys"
    },
    {
      "name": "Branding"
    },
    {
      "name": "Events"
    },
    {
      "name": "Webhooks"
    }
  ]
}
