Blueticked Conversions API

Operations for conversions.

Operations 1

POST /api/v1/conversions Record a conversion #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/blueticked-com:blueticked-com-conversions-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

blueticked-com-conversions-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Blueticked Conversions API
  version: 1.0.0
  description: 'Multi-channel messaging (WhatsApp, SMS, email), templates, flows, campaigns, per-recipient documents and webhooks for South African businesses, plus contacts, flows, AI agents, journeys, published sites and a commerce layer with products, orders and checkout. One send endpoint (`POST /api/v1/messages`) covers all three channels. See the request examples for the WhatsApp template, SMS, and email shapes. Authenticate every request with `Authorization: Bearer blu_live_...` (or `blu_test_...` for the sandbox). Send an `Idempotency-Key` header on the POSTs that honour it (the ones whose OpenAPI operation declares the `Idempotency-Key` parameter), and a retry of the same key replays the original response instead of repeating the write. Everywhere else the header is IGNORED, so a blind retry of a 5xx can create a second record. 29 of 48 documented POST operations deduplicate; the rest do not. ## Test mode


    Keys prefixed `blu_test_` are sandbox keys: sends never reach providers,

    never bill, and have their delivery lifecycle simulated (queued → sent →

    delivered → read, ~5s per step). Webhook deliveries fire with `"test": true`. Separately, a `test_mode: true` field on a send is a dry run that validates the

    request and returns `status: "validated"` without creating or sending anything;

    it works with any key, including a live one. Deterministic magic numbers:


    | Number | Behaviour |

    |---|---|

    | +27800000001 | Always delivers (any other number behaves the same) |

    | +27800000002 | Fails at the send step with `recipient_unreachable` |

    | +27800000003 | Delivers, then fires a simulated `message.received` reply |


    A test key''s writes are real workspace data: a contact it creates is stored like any other, where a live

    campaign could reach it. A live key refuses to send to the sandbox numbers (+27800000000 to +27800000099)

    from `POST /api/v1/messages` with 400 `validation_failed`, but a campaign or a flow does not check

    the number, so delete test contacts when you go live. ## Error codes


    | Code | Meaning |

    |---|---|

    | `invalid_field_map` | The inbound field map has an unknown or ambiguous target or repeats a contact identity target. |

    | `unauthorized` | The credential was refused: a missing or malformed Authorization header, an unknown key or a wrong secret, a revoked or expired key, an expired or revoked OAuth token, or an inactive workspace. Once a key''s secret checks out, details.reason says which (revoked, expired, workspace_inactive). |

    | `forbidden` | The credential is valid but not allowed here: a missing scope (details.required), an address outside the key''s IP allowlist, a business-restricted key on an endpoint it cannot use, or a blu_pub_ key off the agent chat endpoint or its allowed origins. |

    | `not_found` | The requested contact, template, message or campaign does not exist. |

    | `validation_failed` | The request was refused before anything happened. Either the body does not fit the schema (details is the list of schema issues, each with the field''s path), or a value breaks a rule (details names the field). |

    | `rate_limited` | The workspace or key exceeded a rate limit, or the calling address made too many failed authentication attempts. Wait Retry-After seconds, then retry. |

    | `idempotency_conflict` | The same Idempotency-Key was reused with a different body. |

    | `idempotency_in_progress` | A request with the same Idempotency-Key is still being processed; retry once it finishes. |

    | `resource_in_use` | The resource cannot be deleted because other live resources still depend on it (e.g. a group targeted by a scheduled campaign). `details` lists the dependents. |

    | `conflict` | The other kind of 409: the request collides with what already exists (a code or name that is taken, or a resource in a state the call does not accept, as when only a paid order can book a courier).'
  contact:
    name: Blueticked Developer Support
    url: https://blueticked.com/docs
    email: developers@blueticked.com
servers:
- url: https://app.blueticked.com
  description: Production
security:
- bearerAuth: []
tags:
- name: conversions
  description: Operations for conversions.
paths:
  /api/v1/conversions:
    post:
      operationId: post-conversion
      summary: Record a conversion
      description: 'Report a conversion (an order, booking or any success event). Blueticked attributes it to the contact''s most recent email click (falling back to the most recent delivery) within a 7-day window, and rolls the value up on the campaign and reports surfaces.


        The JSON request body is limited to 32 KiB (32,768 bytes). A larger body answers 413 `payload_too_large`; invalid JSON answers 400 `validation_failed`.


        Identify the contact with `contact_id` (UUID) or `contact_email` (matched case-insensitively). An unknown contact is stored UNATTRIBUTED and counted: never dropped, never an error.


        `value_cents` is a non-negative integer in the currency''s minor unit; `currency` is a 3-letter ISO code (default ZAR). Mixed-currency totals are always reported as a per-currency breakdown, never summed.


        Attribution is last-touch: the most recent email CLICK in the 7 days before `occurred_at`, falling back to the most recent delivery. `attributed: false` means no email engagement fell inside the window.


        `external_ref` makes ingestion idempotent: replaying the same reference returns the stored conversion (200) instead of double-counting.


        Values are your own reported figures. Blueticked attributes them but never verifies them, and they are unrelated to Blueticked billing.'
      tags:
      - conversions
      security:
      - bearerAuth:
        - conversions:write
      x-required-scopes:
      - conversions:write
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                contact_email:
                  type: string
                  example: buyer@example.com
                  format: email
                value_cents:
                  type: number
                  example: 45000
                currency:
                  type: string
                  example: ZAR
                occurred_at:
                  type: string
                  example: '2026-07-16T20:00:00Z'
                  format: date-time
                source:
                  type: string
                  example: shopify
                external_ref:
                  type: string
                  example: order-1001
              required:
              - value_cents
            examples:
              post-conversion:
                summary: Record a conversion
                value:
                  contact_email: buyer@example.com
                  value_cents: 45000
                  currency: ZAR
                  occurred_at: '2026-07-16T20:00:00Z'
                  source: shopify
                  external_ref: order-1001
      responses:
        '200':
          description: Existing conversion returned for a deduplicated external_ref
          headers:
            x-ratelimit-limit:
              description: Requests permitted in the current window.
              schema:
                type: integer
            x-ratelimit-remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            x-ratelimit-reset:
              description: When the current window resets.
              schema:
                type: string
                format: date-time
          content:
            application/json:
              schema:
                type: object
                properties:
                  conversion:
                    type: object
                    properties:
                      id:
                        type: string
                        example: 59c73d94-68e7-44fd-bbb5-2c7d94ffb2f1
                        format: uuid
                      contact_id:
                        type:
                        - string
                        - 'null'
                        example: ff506e53-e2f0-4d92-884a-dab77d628bad
                        format: uuid
                      campaign_id:
                        type:
                        - string
                        - 'null'
                        example: 8701a57c-11df-4a63-a950-0f22206d6bdf
                        format: uuid
                      message_id:
                        type:
                        - string
                        - 'null'
                        example: 763c4dc3-d46b-4c10-8bd4-3f233a933ea0
                        format: uuid
                      value_cents:
                        type: number
                        example: 45000
                      currency:
                        type: string
                        example: ZAR
                      occurred_at:
                        type: string
                        example: '2026-07-16T20:00:00.000Z'
                        format: date-time
                      source:
                        type:
                        - string
                        - 'null'
                        example: shopify
                      external_ref:
                        type:
                        - string
                        - 'null'
                        example: order-1001
                      attributed:
                        type: boolean
                        example: true
                    required:
                    - id
                    - contact_id
                    - campaign_id
                    - message_id
                    - value_cents
                    - currency
                    - occurred_at
                    - source
                    - external_ref
                    - attributed
                  deduplicated:
                    type: boolean
                    example: false
                  request_id:
                    type: string
                    example: req_b2e3d610e10f1b07468721ee98f0e1d2
                required:
                - conversion
                - deduplicated
                - request_id
              examples:
                post-conversion:
                  summary: Idempotent replay
                  value:
                    conversion:
                      id: 59c73d94-68e7-44fd-bbb5-2c7d94ffb2f1
                      contact_id: ff506e53-e2f0-4d92-884a-dab77d628bad
                      campaign_id: 8701a57c-11df-4a63-a950-0f22206d6bdf
                      message_id: 763c4dc3-d46b-4c10-8bd4-3f233a933ea0
                      value_cents: 45000
                      currency: ZAR
                      occurred_at: '2026-07-16T20:00:00.000Z'
                      source: shopify
                      external_ref: order-1001
                      attributed: true
                    deduplicated: true
                    request_id: req_b2e3d610e10f1b07468721ee98f0e1d2
        '201':
          description: Success
          headers:
            x-ratelimit-limit:
              description: Requests permitted in the current window.
              schema:
                type: integer
            x-ratelimit-remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            x-ratelimit-reset:
              description: When the current window resets.
              schema:
                type: string
                format: date-time
          content:
            application/json:
              schema:
                type: object
                properties:
                  conversion:
                    type: object
                    properties:
                      id:
                        type: string
                        example: 59c73d94-68e7-44fd-bbb5-2c7d94ffb2f1
                        format: uuid
                      contact_id:
                        type:
                        - string
                        - 'null'
                        example: ff506e53-e2f0-4d92-884a-dab77d628bad
                        format: uuid
                      campaign_id:
                        type:
                        - string
                        - 'null'
                        example: 8701a57c-11df-4a63-a950-0f22206d6bdf
                        format: uuid
                      message_id:
                        type:
                        - string
                        - 'null'
                        example: 763c4dc3-d46b-4c10-8bd4-3f233a933ea0
                        format: uuid
                      value_cents:
                        type: number
                        example: 45000
                      currency:
                        type: string
                        example: ZAR
                      occurred_at:
                        type: string
                        example: '2026-07-16T20:00:00.000Z'
                        format: date-time
                      source:
                        type:
                        - string
                        - 'null'
                        example: shopify
                      external_ref:
                        type:
                        - string
                        - 'null'
                        example: order-1001
                      attributed:
                        type: boolean
                        example: true
                    required:
                    - id
                    - contact_id
                    - campaign_id
                    - message_id
                    - value_cents
                    - currency
                    - occurred_at
                    - source
                    - external_ref
                    - attributed
                  deduplicated:
                    type: boolean
                    example: false
                  request_id:
                    type: string
                    example: req_b2e3d610e10f1b07468721ee98f0e1d2
                required:
                - conversion
                - deduplicated
                - request_id
              examples:
                post-conversion:
                  summary: Record a conversion
                  value:
                    conversion:
                      id: 59c73d94-68e7-44fd-bbb5-2c7d94ffb2f1
                      contact_id: ff506e53-e2f0-4d92-884a-dab77d628bad
                      campaign_id: 8701a57c-11df-4a63-a950-0f22206d6bdf
                      message_id: 763c4dc3-d46b-4c10-8bd4-3f233a933ea0
                      value_cents: 45000
                      currency: ZAR
                      occurred_at: '2026-07-16T20:00:00.000Z'
                      source: shopify
                      external_ref: order-1001
                      attributed: true
                    deduplicated: false
                    request_id: req_b2e3d610e10f1b07468721ee98f0e1d2
        '400':
          description: Validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: 'Unauthorized: missing or invalid API key'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '402':
          description: Insufficient wallet balance
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: 'Forbidden: scope, plan limit, or suspension'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Conflict (idempotency-key reuse or resource in use)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: Request body above the endpoint's size limit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: 'Understood but refused: the request is well-formed, the action is not permitted for this resource'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            x-ratelimit-limit:
              description: Requests permitted in the current window.
              schema:
                type: integer
            x-ratelimit-remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            x-ratelimit-reset:
              description: When the current window resets.
              schema:
                type: string
                format: date-time
            Retry-After:
              description: Whole seconds to wait before retrying (at least 1); the same moment as x-ratelimit-reset.
              schema:
                type: integer
                minimum: 1
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '502':
          description: An upstream provider refused or failed the request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: Channel or send queue unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    Error:
      type: object
      required:
      - ok
      - error
      properties:
        ok:
          type: boolean
          enum:
          - false
          example: false
        error:
          type: object
          required:
          - code
          - message
          - next_steps
          properties:
            code:
              type: string
              enum:
              - invalid_field_map
              - unauthorized
              - forbidden
              - not_found
              - validation_failed
              - rate_limited
              - idempotency_conflict
              - idempotency_in_progress
              - resource_in_use
              - conflict
              - method_not_allowed
              - gone
              - channel_unavailable
              - queue_unavailable
              - insufficient_balance
              - spend_cap_reached
              - plan_limit_reached
              - subscription_inactive
              - variables_mismatch
              - button_params_mismatch
              - document_invalid
              - document_required
              - document_not_applicable
              - document_source_invalid
              - document_source_required
              - document_source_not_applicable
              - template_kind_unsupported
              - internal_error
              - payload_too_large
              - service_unavailable
              - provider_error
              - enable_requires_dashboard
              - activation_requires_dashboard
              - journey_not_active
              description: Stable machine-readable error code.
            message:
              type: string
              description: Human-readable explanation.
            details:
              type: object
              additionalProperties: true
              description: Optional structured validation detail.
              properties:
                reason:
                  type: string
                  description: 'Which failure of this code it was, when the API says. Known values:

                    - promotions_not_ready (conflict)

                    - promotion_invalid (validation_failed)

                    - automatic_promotion_limit (validation_failed)

                    - promotion_products_limit (validation_failed)

                    - promotion_target_missing (conflict)

                    - promotion_version_conflict (conflict)

                    - promotion_not_found (not_found)

                    - discount_code_taken (conflict)

                    - promotion_read_failed (service_unavailable)

                    - promotion_save_failed (service_unavailable)

                    - invalid_form (validation_failed)

                    - missing_file (validation_failed)

                    - invalid_contact_id (validation_failed)

                    - file_too_large (validation_failed)

                    - not_a_pdf (validation_failed)

                    - document_not_found (not_found)

                    - document_not_for_contact (validation_failed)

                    - invalid_form (validation_failed)

                    - missing_file (validation_failed)

                    - file_too_large (payload_too_large)

                    - unsupported_mime (validation_failed)

                    - invalid_file (validation_failed)

                    - quota_exceeded (validation_failed)

                    - upload_failed (internal_error)

                    - media_upload_expired (conflict)

                    - media_upload_invalid (validation_failed)

                    - media_upload_mismatch (validation_failed)

                    - media_upload_unavailable (service_unavailable)

                    - media_upload_action_invalid (validation_failed)

                    - payload_too_large (payload_too_large)

                    - body_read_failed (validation_failed)

                    - invalid_json (validation_failed)

                    - register_failed (validation_failed)

                    - status_unconfirmed (validation_failed)

                    - status_mismatch (validation_failed)

                    - status_read_failed (validation_failed)

                    - managed_by_studio (conflict)

                    - rotation_grace_active (conflict)

                    - key_revoked (conflict)

                    - list_not_found (not_found)

                    - segment_not_found (not_found)

                    - setup_recheck_too_soon (rate_limited)

                    - chat_already_answered (conflict)

                    - consent_evidence_unavailable (service_unavailable)

                    - audience_unchecked (service_unavailable)

                    - erasure_retrying (service_unavailable)

                    - sms_channel_disconnected (channel_unavailable)

                    - sms_channel_check_unavailable (channel_unavailable)

                    - revoked (unauthorized)

                    - expired (unauthorized)

                    - workspace_inactive (unauthorized)

                    - no_draft (validation_failed)

                    - invalid_draft (validation_failed)

                    - live_address_unavailable (service_unavailable)

                    - publish_failed (internal_error)

                    - hosting_charge_required (plan_limit_reached)

                    - hosting_charge_unaffordable (insufficient_balance)

                    - not_published (validation_failed)

                    - unpublish_failed (internal_error)

                    - business_not_found (not_found)

                    - business_limit_reached (plan_limit_reached)

                    - business_charge_required (plan_limit_reached)

                    - business_charge_changed (plan_limit_reached)

                    - business_charge_live_key_required (plan_limit_reached)

                    - operator_agreement_unsigned (forbidden)

                    - no_organization (forbidden)

                    - unknown_template (validation_failed)

                    - slug_unavailable (conflict)

                    - insert_failed (internal_error)

                    - recipient_not_found (not_found)

                    - consent_required (validation_failed)

                    - custom_identity_required (validation_failed)

                    - dmarc_required (validation_failed)

                    - business_identity_required (validation_failed)

                    - physical_address_required (validation_failed)

                    - freemail_reply_to (validation_failed)

                    - reply_to_not_aligned (validation_failed)

                    - contacts_write_required (forbidden)

                    - domain_in_use_elsewhere (conflict)

                    - domain_in_use_by_business (conflict)

                    - domain_registered_unclaimed (conflict)

                    - domain_holder_unchecked (service_unavailable)

                    - receive_only (conflict)

                    - custom_field_creation_forbidden (forbidden)

                    - action_invalid (validation_failed)

                    - base_url_required (conflict)

                    - key_taken (conflict)

                    - too_many_actions (conflict)

                    - website_form_channel (conflict)

                    - invalid_contact_ids (validation_failed)

                    - business_id_required (validation_failed)

                    - automations_retired (gone)

                    - studio_flow_limit_reached (conflict)

                    - studio_flow_count_unavailable (service_unavailable)

                    - definition_required (validation_failed)

                    - invalid_business_id (validation_failed)

                    - invalid_definition (validation_failed)

                    - compile_failed (validation_failed)

                    - sms_type_required (validation_failed)

                    - edit_version_required (validation_failed)

                    - conflict (conflict)

                    - studio_flow_live (resource_in_use)

                    - studio_flow_shared_entry (resource_in_use)

                    - journey_read_failed (service_unavailable)

                    - field_read_failed (service_unavailable)

                    - sequence_requires_send_step (validation_failed)

                    - invalid_trigger (validation_failed)

                    - flow_name_taken (conflict)

                    - invalid_steps (validation_failed)

                    - invalid_segment (validation_failed)

                    - invalid_email_template (validation_failed)

                    - invalid_whatsapp_template (validation_failed)

                    - unsupported_whatsapp_template (validation_failed)

                    - whatsapp_template_not_approved (validation_failed)

                    - whatsapp_template_not_attached (validation_failed)

                    - not_found (not_found)

                    - forbidden (forbidden)

                    - reference_outside_business (validation_failed)

                    - meta_refused (provider_error)

                    - journeys_list_failed (internal_error)

                    - ai_agents_list_failed (internal_error)

                    - form_submissions_list_failed (internal_error)

                    - studio_flow_save_failed (internal_error)

                    - automation_save_failed (internal_error)

                    - save_failed (internal_error)

                    - table_not_found (not_found)

                    - table_has_no_people (validation_failed)

                    - table_status_unknown (validation_failed)

                    - table_column_unknown (validation_failed)

                    - row_field_needs_table (validation_failed)

                    - table_unchecked (service_unavailable)

                    - assignee_not_member (validation_failed)

                    - assignee_cannot_open (validation_failed)

                    - media_pending (conflict)

                    - media_unavailable (not_found)

                    - not_whatsapp_channel (validation_failed)

                    - whatsapp_token_missing (channel_unavailable)

                    - meta_unavailable (provider_error)

                    - meta_unavailable (service_unavailable)

                    - meta_pricing_permission_required (provider_error)

                    Treat any other value as the code''s general case.'
            next_steps:
              type: array
              description: What to do about it, as data. `message` tells a person what happened; this tells a program what to do next. Always present and never empty. Branch on `action`; never parse `description`.
              items:
                type: object
                required:
                - action
                - by
                - description
                properties:
                  action:
                    type: string
                    description: Stable verb identifying the remedy, e.g. connect_channel, retry_after, grant_missing_scope.
                    example: connect_channel
                  by:
                    type: string
                    enum:
                    - caller
                    - human
                    - waiting
                    description: 'WHO can clear it. `caller`: fix it in code and retry. `human`: a person must act in a browser; surface it and do NOT retry, because no rewrite of this request will succeed. `waiting`: outside anyone''s control (an upstream review or outage); poll or back off.'
                    example: human
                  description:
                    type: string
                    description: One sentence, for the human reading your logs.
                  endpoint:
                    type: string
                    description: The API call that fixes it, when the fix is a call.
                    example: POST /api/v1/channels/connect-sessions
                  url:
                    type: string
                    description: Where a person goes, when `by` is `human`.
        request_id:
          type: string
          example: req_b2e3d610e10f1b07468721ee98f0e1d2
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: blu_live or blu_test
      description: Your API key as a Bearer token. Use a `blu_test_` key for the sandbox.