BuoyForms
Docs

API Reference

API Reference

Access BuoyForms programmatically with our REST API.

Authentication

All API requests require an API key passed in the Authorization header:

Authorization: Bearer YOUR_API_KEY

Create API keys in Settings > API Keys.

Base URL

https://buoyforms.com/api/v1

The OpenAPI spec is served at /openapi.yaml.

Rate Limits

Rate limits vary by plan and are enforced per API key:

Plan Requests/Minute
Free 10
Pro 100
Business 500
Enterprise 2,000

Rate Limit Headers

Every API response includes rate limit headers:

Header Description
X-RateLimit-Limit Maximum requests per window
X-RateLimit-Remaining Requests remaining in current window
X-RateLimit-Reset Unix timestamp when limit resets

Handling Rate Limits

When rate limited, the API returns HTTP 429 with a Retry-After header indicating seconds to wait.

{
  "error": "rate_limit_exceeded",
  "message": "Too many requests",
  "retryAfter": 45
}

Best practices:

  • Implement exponential backoff on 429 responses
  • Cache responses where possible
  • Batch operations when supported
  • Monitor your X-RateLimit-Remaining header

Endpoints

Forms

List Forms

GET /forms

Returns up to 100 non-deleted forms scoped to the API key's organization, ordered by updatedAt desc.

Response:

{
  "forms": [
    {
      "id": "form_abc123",
      "title": "Contact Form",
      "description": "",
      "status": "published",
      "createdAt": "2025-01-15T10:00:00Z",
      "updatedAt": "2025-01-20T14:30:00Z"
    }
  ]
}

Get Form

GET /forms/:formId

Returns the form with its full structure (pages, fields, settings).

Create Form

POST /forms

Body:

{
  "title": "Survey Form",
  "description": "Customer feedback survey",
  "slug": "survey",
  "status": "draft",
  "pages": [],
  "fields": [],
  "settings": {}
}

Only title is required. If pages and fields are provided, the form structure is created atomically.

Update Form

PUT /forms/:formId

Updates form metadata (title, description, slug, status). For structural changes (pages, fields, settings), use the structure endpoint below.

Replace Form Structure

PUT /forms/:formId/structure

Atomically replaces the form's pages, fields, and settings.

Body:

{
  "pages": [...],
  "fields": [...],
  "settings": {...}
}

Delete Form

DELETE /forms/:formId

Soft-deletes the form.

Submissions

List Submissions

GET /forms/:formId/submissions

Query Parameters:

  • limit (integer, default 50): Results per page
  • offset (integer, default 0): Pagination offset
  • startDate (ISO date): Filter by submission date
  • endDate (ISO date): Filter by submission date

Get Submission

GET /submissions/:submissionId

Create Submission

POST /forms/:formId/submissions

Used for authenticated programmatic form submissions. This endpoint is authenticated with API keys and does not use Turnstile/CAPTCHA verification.

If you are submitting through a published public form or embed, bot protection is controlled by the form's botProtection settings. When bot protection is enabled, the public submission request must include the CAPTCHA token collected by the public form runtime.

Body:

{
  "data": {
    "fld_email01": "user@example.com",
    "fld_message01": "Hello!"
  },
  "submitterEmail": "user@example.com",
  "submitterName": "Jane Example"
}

Delete Submission

DELETE /submissions/:submissionId

Fields: there is no separate /fields CRUD in v1. Manage fields through the PUT /forms/:formId/structure endpoint, which replaces the pages/fields/settings atomically.

Webhooks

List Webhooks

GET /webhooks

List accepts an optional ?formId= query parameter to filter by form.

Create Webhook

POST /webhooks

Body:

{
  "name": "My webhook",
  "url": "https://your-app.com/webhook",
  "events": ["submission.created"],
  "formId": "form_abc123",
  "enabled": true
}

formId is optional — omit it for an organization-wide webhook. name, url, and events are required.

Update Webhook

PUT /webhooks/:webhookId

Delete Webhook

DELETE /webhooks/:webhookId

Error Responses

Errors return appropriate HTTP status codes with JSON body:

{
  "error": "validation_error",
  "message": "Invalid request body",
  "details": [
    {
      "field": "email",
      "message": "Invalid email format"
    }
  ]
}
Status Description
400 Bad Request - Invalid parameters
401 Unauthorized - Invalid or missing API key
403 Forbidden - Insufficient permissions
404 Not Found - Resource doesn't exist
429 Too Many Requests - Rate limit exceeded
500 Internal Server Error

Webhooks Signature Verification

Verify webhook authenticity using the X-Signature header:

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

SDK Support

Official SDKs coming soon:

  • JavaScript/TypeScript
  • Python
  • Ruby
  • Go

Changelog

  • 2025-11-27: Added rate limiting headers and documentation
  • 2025-11-20: Initial API release