{
  "openapi": "3.1.0",
  "info": {
    "title": "Brinkpost API",
    "version": "1.14.0",
    "summary": "Send product events, read campaign metrics.",
    "description": "The Brinkpost API records what your users do (events) so campaigns start and stop at the right moment, and reports how your campaigns deliver. Authenticate with your workspace's API key: as `api_key` in the JSON body of a POST, or as `Authorization: Bearer <key>` on any request. Keys start with `bp_live_` and are created in the console at https://app.brinkpostapp.com/settings/api-keys.",
    "contact": {
      "name": "Brinkpost support",
      "email": "support@brinkpost.com",
      "url": "https://brinkpost.com/docs"
    },
    "termsOfService": "https://brinkpost.com/legal/terms"
  },
  "servers": [
    {
      "url": "https://api.brinkpostapp.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Events",
      "description": "Record what your users do."
    },
    {
      "name": "Campaigns",
      "description": "Your campaigns and their delivery metrics."
    },
    {
      "name": "Workspace",
      "description": "The workspace and key a request is made with."
    }
  ],
  "paths": {
    "/v1/events": {
      "post": {
        "tags": [
          "Events"
        ],
        "operationId": "sendEvent",
        "summary": "Send an event",
        "description": "Records one event for a contact, identified by email. Campaigns whose exit event this is stop for that contact (the answer lists them in `campaign_exits`). The key can travel in the body as `api_key` (shown here) or in an `Authorization: Bearer` header.",
        "security": [
          {
            "bearerAuth": []
          },
          {}
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendEventRequest"
              },
              "example": {
                "api_key": "bp_live_••••••••••••••••••••••••••••••••",
                "event_name": "plan_upgraded",
                "email": "jordan@acmecorp.io",
                "user_id": "usr_2841",
                "properties": {
                  "plan": "pro",
                  "previous_plan": "starter",
                  "mrr": 49
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventAccepted"
                },
                "example": {
                  "event_id": "evt_k3v9q2m8x1w7p4zt",
                  "status": "accepted",
                  "received_at": "2026-11-10T14:02:11.482Z",
                  "campaign_exits": [
                    {
                      "campaign_id": "cmp_8hq2v7xk1m",
                      "name": "Win-back"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "listEvents",
        "summary": "List recent events",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "email",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Only this contact's events."
          },
          {
            "name": "event_name",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The workspace's events, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Event"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/events/batch": {
      "post": {
        "tags": [
          "Events"
        ],
        "operationId": "sendEventBatch",
        "summary": "Send events in a batch",
        "description": "Records up to 1,000 events (and 5 MB) in one request; the whole batch is accepted or refused. The body is `{ \"api_key\": ..., \"events\": [...] }`, or a bare array of events with the key in an `Authorization: Bearer` header.",
        "security": [
          {
            "bearerAuth": []
          },
          {}
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendBatchRequest"
              },
              "example": {
                "api_key": "bp_live_••••••••••••••••••••••••••••••••",
                "events": [
                  {
                    "event_name": "plan_upgraded",
                    "email": "jordan@acmecorp.io",
                    "properties": {
                      "plan": "pro"
                    }
                  },
                  {
                    "event_name": "seat_added",
                    "email": "lee@acmecorp.io",
                    "properties": {
                      "seats": 6
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchAccepted"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "413": {
            "description": "More than 1,000 events, or more than 5 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "type": "invalid_request_error",
                    "code": "batch_too_large",
                    "message": "This batch has 1200 events; a batch holds at most 1000. Split it into several requests.",
                    "param": "events",
                    "limit": 1000,
                    "doc_url": "https://brinkpost.com/docs/errors#batch_too_large"
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/events/{id}": {
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "getEvent",
        "summary": "Get an event",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^evt_[a-z0-9]{16}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The event.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/campaigns": {
      "get": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "listCampaigns",
        "summary": "List campaigns",
        "responses": {
          "200": {
            "description": "The workspace's campaigns.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Campaign"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/campaigns/{id}": {
      "get": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "getCampaign",
        "summary": "Get a campaign",
        "parameters": [
          {
            "$ref": "#/components/parameters/CampaignPath"
          }
        ],
        "responses": {
          "200": {
            "description": "The campaign, with how many contacts left it through an exit event.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Campaign"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "exits": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/metrics": {
      "get": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "getMetrics",
        "summary": "Campaign metrics",
        "description": "Sent, delivered, opened and clicked counts per campaign over a period, with daily figures. `delivery_rate` is delivered / sent and `open_rate` is unique opens / delivered, as fractions (0.9812 = 98.12 %). Days are UTC; figures are complete up to yesterday.",
        "parameters": [
          {
            "name": "campaign_id",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^cmp_[a-z0-9]{10}$"
            },
            "description": "One campaign; all campaigns when left out."
          },
          {
            "$ref": "#/components/parameters/Period"
          },
          {
            "$ref": "#/components/parameters/From"
          },
          {
            "$ref": "#/components/parameters/To"
          }
        ],
        "responses": {
          "200": {
            "description": "Metrics.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Metrics"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    },
    "/v1/campaigns/{id}/metrics": {
      "get": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "getCampaignMetrics",
        "summary": "One campaign's metrics",
        "parameters": [
          {
            "$ref": "#/components/parameters/CampaignPath"
          },
          {
            "$ref": "#/components/parameters/Period"
          },
          {
            "$ref": "#/components/parameters/From"
          },
          {
            "$ref": "#/components/parameters/To"
          }
        ],
        "responses": {
          "200": {
            "description": "Metrics.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Metrics"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/workspace": {
      "get": {
        "tags": [
          "Workspace"
        ],
        "operationId": "getWorkspace",
        "summary": "The current workspace",
        "responses": {
          "200": {
            "description": "The workspace and the key used.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workspace"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/keys/revoke": {
      "post": {
        "tags": [
          "Workspace"
        ],
        "operationId": "revokeKey",
        "summary": "Revoke the current key",
        "description": "Revokes the key the request is made with, at once (a key that leaked, or one being rotated out). Create its replacement in the console first.",
        "responses": {
          "200": {
            "description": "Revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "revoked": {
                      "type": "boolean"
                    },
                    "revoked_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key (bp_live_...). On POST requests it may instead be sent as `api_key` in the JSON body."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "maxLength": 255
        },
        "description": "A unique key for this request (also accepted as `idempotency_key` in the body). A key used again within 24 hours is refused with 409 and nothing is recorded twice."
      },
      "CampaignPath": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^cmp_[a-z0-9]{10}$"
        }
      },
      "Period": {
        "name": "period",
        "in": "query",
        "schema": {
          "type": "string",
          "enum": [
            "last_7_days",
            "last_week",
            "last_30_days"
          ],
          "default": "last_7_days"
        },
        "description": "last_7_days: the seven full days before today. last_week: the previous Monday to Sunday. Ignored when from and to are given."
      },
      "From": {
        "name": "from",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "First day, YYYY-MM-DD (with to; at most 93 days)."
      },
      "To": {
        "name": "to",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "Last day, YYYY-MM-DD, inclusive."
      }
    },
    "schemas": {
      "EventFields": {
        "type": "object",
        "required": [
          "event_name",
          "email"
        ],
        "properties": {
          "event_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "What happened, at most 64 characters (plan_upgraded, trial_started ...)."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "The contact the event belongs to. Required."
          },
          "user_id": {
            "type": "string",
            "description": "Your own id for the user."
          },
          "properties": {
            "type": "object",
            "additionalProperties": true,
            "description": "Anything about the event, up to 32 KB of JSON. Usable in campaign filters and templates."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "When it happened; now when left out."
          }
        }
      },
      "SendEventRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EventFields"
          },
          {
            "type": "object",
            "properties": {
              "api_key": {
                "type": "string",
                "description": "Your API key, when it is not sent as a bearer."
              },
              "idempotency_key": {
                "type": "string",
                "maxLength": 255
              }
            }
          }
        ]
      },
      "SendBatchRequest": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "events"
            ],
            "properties": {
              "api_key": {
                "type": "string",
                "description": "Your API key, when it is not sent as a bearer."
              },
              "idempotency_key": {
                "type": "string",
                "maxLength": 255
              },
              "events": {
                "type": "array",
                "minItems": 1,
                "maxItems": 1000,
                "items": {
                  "$ref": "#/components/schemas/EventFields"
                }
              }
            }
          },
          {
            "type": "array",
            "minItems": 1,
            "maxItems": 1000,
            "items": {
              "$ref": "#/components/schemas/EventFields"
            }
          }
        ]
      },
      "CampaignExit": {
        "type": "object",
        "properties": {
          "campaign_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "EventAccepted": {
        "type": "object",
        "required": [
          "event_id",
          "status"
        ],
        "properties": {
          "event_id": {
            "type": "string",
            "pattern": "^evt_[a-z0-9]{16}$",
            "description": "The event's id: evt_ followed by 16 lowercase letters and digits."
          },
          "status": {
            "type": "string",
            "const": "accepted"
          },
          "received_at": {
            "type": "string",
            "format": "date-time"
          },
          "campaign_exits": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CampaignExit"
            },
            "description": "Campaigns this event ended for the contact."
          }
        }
      },
      "BatchAccepted": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "const": "accepted"
          },
          "accepted": {
            "type": "integer"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventAccepted"
            }
          }
        }
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^evt_[a-z0-9]{16}$"
          },
          "event_name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "user_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "properties": {
            "type": "object",
            "additionalProperties": true
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "received_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Campaign": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^cmp_[a-z0-9]{10}$"
          },
          "name": {
            "type": "string"
          },
          "channel": {
            "type": "string",
            "enum": [
              "email",
              "push",
              "in_app"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "draft"
            ]
          },
          "exit_events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Events that end the campaign for a contact."
          },
          "started_on": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "Metrics": {
        "type": "object",
        "properties": {
          "period": {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "format": "date"
              },
              "to": {
                "type": "string",
                "format": "date"
              },
              "name": {
                "type": "string"
              },
              "timezone": {
                "type": "string"
              }
            }
          },
          "campaigns": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "campaign_id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "channel": {
                  "type": "string"
                },
                "sent": {
                  "type": "integer"
                },
                "delivered": {
                  "type": "integer"
                },
                "opened": {
                  "type": "integer",
                  "description": "Unique opens."
                },
                "clicked": {
                  "type": "integer",
                  "description": "Unique clicks."
                },
                "bounced": {
                  "type": "integer"
                },
                "delivery_rate": {
                  "type": "number",
                  "description": "delivered / sent"
                },
                "open_rate": {
                  "type": "number",
                  "description": "opened / delivered"
                },
                "click_rate": {
                  "type": "number",
                  "description": "clicked / delivered"
                },
                "daily": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "date": {
                        "type": "string",
                        "format": "date"
                      },
                      "sent": {
                        "type": "integer"
                      },
                      "delivered": {
                        "type": "integer"
                      },
                      "opened": {
                        "type": "integer"
                      },
                      "clicked": {
                        "type": "integer"
                      },
                      "bounced": {
                        "type": "integer"
                      },
                      "delivery_rate": {
                        "type": "number"
                      },
                      "open_rate": {
                        "type": "number"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Workspace": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "growth",
              "scale",
              "enterprise"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "key": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "display": {
                "type": "string"
              },
              "label": {
                "type": "string"
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "type",
              "code",
              "message"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "invalid_request_error",
                  "authentication_error",
                  "idempotency_error",
                  "rate_limit_error",
                  "permission_error",
                  "api_error"
                ]
              },
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "param": {
                "type": "string"
              },
              "doc_url": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "No key, an unknown key (`invalid_api_key`) or a revoked key (`key_revoked`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "authentication_error",
                "code": "key_revoked",
                "message": "This API key was revoked on 2026-11-09. Create a new key in the console (https://app.brinkpostapp.com/settings/api-keys) and use it instead.",
                "doc_url": "https://brinkpost.com/docs/errors#key_revoked"
              }
            }
          }
        }
      },
      "Invalid": {
        "description": "A field is missing or invalid: `email_required`, `event_name_too_long` (over 64 characters), `invalid_email`, `invalid_properties` ...",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "type": "invalid_request_error",
                "code": "email_required",
                "message": "email is required: every event belongs to a contact, identified by email.",
                "param": "email",
                "doc_url": "https://brinkpost.com/docs/errors#email_required"
              }
            }
          }
        }
      },
      "IdempotencyConflict": {
        "description": "This idempotency key was already used in the last 24 hours.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Over the plan's requests per second.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such object in this workspace.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
