Benchmark Email · OpenAPI Overlay 1.0.0

API Evangelist enhancements for the Benchmark Email v1 API

12 actions 12 updates update extends openapi/benchmark-email-api-openapi.json
Generated by API Evangelist Written by API Evangelist tooling for Benchmark Email's API. It is a proposal applied on top of the contract, not a document Benchmark Email publishes.
View Overlay File View on GitHub Overlay Specification

What the actions change

x-agent-notecontacttermsOfServicevariablesx-rate-limitsx-error-envelopex-api-key-scopesx-concurrency

Targets 7

$.info
$.servers[0]
$
$.paths['/api/contact-structure'].get
$.paths['/api/contact'].get
$.paths['/api/contact/search'].post
$.paths['/api/contact/{contactId}'].delete

OpenAPI Overlay

Raw ↑
overlay: 1.0.0
info:
  title: API Evangelist enhancements for the Benchmark Email v1 API
  version: 1.0.0
x-provenance:
  generated: '2026-08-13'
  method: generated
  source: >-
    Enhancements derived from https://developers.benchmarkemail.io/introduction.md,
    /authentication.md, /rate-limits.md, /errors.md and
    /.well-known/agent-skills/benchmarkinternetgroup/skill.md, applied over the provider's
    own openapi/benchmark-email-api-openapi.json without mutating it.
  note: >-
    Every value written by this overlay is published by Benchmark Email somewhere in its
    documentation. The overlay's job is to move those facts INTO the contract, where a
    generated client or an agent can actually read them. Nothing here is invented.
extends: openapi/benchmark-email-api-openapi.json
actions:
- target: $.info
  description: >-
    Record contact, licence and terms, which the published spec omits entirely.
  update:
    contact:
      name: Benchmark Email Developer Documentation
      url: https://developers.benchmarkemail.io/
    termsOfService: https://www.benchmarkemail.com/terms-of-use/
- target: $.servers[0]
  description: >-
    The published server is the variable {apiBaseUrl} with an EMPTY default, so a generated
    client will not compile until the account owner pastes their base URL in. The
    documentation states the real shape — https://api-{region}-{cluster}.benchmarkemail.io,
    for example https://api-us-west-2-a.benchmarkemail.io — so give the variable a usable
    default and an enum-free description that names the pattern.
  update:
    variables:
      apiBaseUrl:
        default: https://api-us-west-2-a.benchmarkemail.io
        description: >-
          Your account's regional API base URL, of the form
          https://api-{region}-{cluster}.benchmarkemail.io. Copy the exact value from
          Settings > API Keys in your Benchmark Email account. The default shown is the
          documentation's us-west-2-a example and will not work for accounts in another
          region or cluster.
- target: $
  description: >-
    Declare the account-wide rate limits and the response headers that signal them, so a
    client can plan for them without reading the prose page.
  update:
    x-rate-limits:
      hourly:
        limit: 3600
        window: 1 hour
        scope: account (shared across all API keys)
      monthly:
        limit: contact limit x 10 (free plans) / x 100 (paid plans)
        window: billing period
        scope: account
      response_headers:
      - X-RateLimit-Limit
      - X-RateLimit-Remaining
      - X-RateLimit-Reset
      - X-Monthly-Limit
      - X-Monthly-Remaining
      exhaustion:
        status: 429
        header: Retry-After
        backoff: min(Retry-After * 2^attempt, 300)
      source: https://developers.benchmarkemail.io/rate-limits
- target: $
  description: >-
    Declare the error envelope. The spec declares 400/401/403/404/500 status codes with no
    schema anywhere, so a client has no way to know errors arrive as an array.
  update:
    x-error-envelope:
      format: proprietary
      rfc9457: false
      content_type: application/json
      shape: '{"errors": [{"errorType": "...", "message": "...", "field": "..."}]}'
      switch_on: errorType
      known_types:
      - ValidationError
      - DuplicateFieldError
      - ConcurrencyError
      - UnauthorizedError
      - ForbiddenError
      - RecordNotFound
      - TooManyRequestsError
      catalog: errors/benchmark-email-problem-types.yml
      source: https://developers.benchmarkemail.io/errors
- target: $
  description: >-
    Declare the API-key scope model. The spec's single apiKeyAuth scheme carries no scopes
    and no per-operation security, so the authorization model is invisible to any tool
    reading the contract.
  update:
    x-api-key-scopes:
      format: '{resource}:{access}'
      header: X-API-Key
      key_format: bme_{region}_{43 chars}
      write_implies_read: true
      scopes:
        contacts:read: Read contacts, lists, structures, search and export
        contacts:write: Create, update and delete contacts, lists and structures
        campaigns:read: Read campaigns and browse templates
        campaigns:write: Create, update, delete and duplicate campaigns
        reports:read: Read dashboard and email performance reports
        domains:read: Read sending domains
      failure:
        status: 403
        errorType: ForbiddenError
        message_names_required_scope: true
      detail: scopes/benchmark-email-scopes.yml
      source: https://developers.benchmarkemail.io/authentication
- target: $
  description: >-
    Declare the concurrency model. __v is required on PUT and PATCH of versioned resources
    and is nowhere in the contract, because no request body carries a schema.
  update:
    x-concurrency:
      style: optimistic locking
      field: __v
      location: request body
      applies_to:
      - put_api_contact_by_contactId
      - patch_api_email_campaign_by_campaignId
      workflow: GET to read the current __v, send it back on the write, increment on success
      failure:
        status: 400
        errorType: ConcurrencyError
      idempotency: >-
        This is conflict detection, NOT idempotency. Benchmark Email publishes no
        Idempotency-Key header; a retried POST creates a duplicate.
- target: $
  description: >-
    Record the capability boundary. Several obvious operations do not exist by design, and
    an agent reading only the spec will keep looking for them.
  update:
    x-capability-boundaries:
      not_available_via_api:
      - Schedule a campaign
      - Send a campaign
      - Cancel a sending campaign
      - Test-send a campaign
      - Verify a new sending domain
      - Reactivate an Inactive contact (compliance)
      - Billing, user-management and admin endpoints
      failure_mode:
        status: 403
        message: This endpoint is not accessible via API key
      source: https://developers.benchmarkemail.io/introduction
- target: $
  description: Link the agent surfaces the provider actually serves.
  update:
    x-agent-surfaces:
      llms_txt: https://developers.benchmarkemail.io/llms.txt
      llms_full_txt: https://developers.benchmarkemail.io/llms-full.txt
      agent_skill: https://developers.benchmarkemail.io/.well-known/agent-skills/benchmarkinternetgroup/skill.md
      agent_card: https://developers.benchmarkemail.io/.well-known/agent-card.json
      mcp_endpoint: https://developers.benchmarkemail.io/mcp
      mcp_note: Documentation-search server; no tool calls this API.
- target: $.paths['/api/contact-structure'].get
  description: >-
    Flag the required entry point. Field ids and the structure id are prerequisites for
    every contact write, and nothing in the spec says so.
  update:
    x-entry-point: true
    x-agent-note: >-
      Call this FIRST. Contact creates and updates reference field definitions by _id from
      this response, and lists are addressed beneath contactStructureId.
- target: $.paths['/api/contact'].get
  description: Warn that this operation is unpaginated.
  update:
    x-pagination: none
    x-agent-note: >-
      Returns every contact with no pagination. Benchmark Email's own Agent Skill limits
      this to small databases; use POST /api/contact/search with page and size for large
      accounts.
- target: $.paths['/api/contact/search'].post
  description: Record that the source array is mandatory, the API's most common 400.
  update:
    x-agent-note: >-
      Requires a non-empty "source" array naming the fields to return, e.g.
      ["_id","key","fields"]. Omitting it returns 400 ValidationError.
- target: $.paths['/api/contact/{contactId}'].delete
  description: Mark the destructive operation.
  update:
    x-destructive: true
    x-reversible: false
    x-agent-note: >-
      Permanent and irreversible, and there is no sandbox to rehearse it in. Require human
      confirmation before calling.