> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inflection.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Submit one product event

> Validates, deduplicates and publishes one product event. Requires `WRITE`
access for Personal Access Tokens and write-capable OAuth users.
The request body is limited to 256 KiB of UTF-8 JSON. A `202` response means
the event was accepted and durably queued.
This request consumes 1 Product Activity rate-limit unit.




## OpenAPI

````yaml /api-reference/openapi.yaml post /v2/product-events
openapi: 3.1.0
info:
  title: Inflection Developer API
  version: 1.1.0
  description: >
    The Inflection Developer API (`/v2`). Authenticate with a Personal Access

    Token (`Authorization: Bearer inf_pat_...`).


    **`/v2` is `/v1` plus more.** Every `/v1` endpoint is served here unchanged,

    so a client can pin to one version and reach the whole API. On top of those

    sit reads `/v1` never had: journeys, journey and email stats with a journey

    rollup, and the email and list collections. `/v1` remains published.


    **Response envelope.** Every response is wrapped:

    `{ "data": …, "pagination": …, "errors": [...], "meta": { "status":
    "SUCCESS|FAILURE", "timestamp": "…" } }`.

    `data` carries the payload, `pagination` appears only on paged lists, and on

    failures `errors` is populated with `meta.status: "FAILURE"`.

    Product Activity ingestion uses a specialized envelope containing only
    `errors`

    and `meta`, with a correlation id in `meta` for support and downstream
    tracing.


    **Query parameters are camelCase**: `pageNumber`, `pageSize`, `startTime`,

    `folderId`. The journeys, emails, lists and stats reads additionally accept

    the snake_case spelling of every scalar parameter (`page_number`,

    `folder_id`, …); the contact, activity and list-member endpoints do not.

    Repeatable parameters also accept a `[]` suffix (`status[]`, `tag[]`).


    **Asynchronous contact writes.** `POST/PATCH /v2/contacts*` are processed

    asynchronously: they return **200** with a `PENDING` transaction

    acknowledgement (not the created/updated contact). Poll

    `GET /v2/contacts/transactions/{transactionId}` for the per-contact result.


    **Status-code quirks** (documented per-operation): a missing **contact** is

    reported as **400** (not 404), whereas a missing **list**, **email** or

    **journey** is **404**. For journeys and emails that also covers a

    soft-deleted record and a malformed id. Nothing returns 201 or 204.


    **Empty results are not errors.** On the paged reads, a `tag`, `folderId` or

    `search` that matches nothing returns an empty page, not a 404.


    **Errors.** Failures use the envelope above

    (`errors[].{errorCode,message,detail}`). Authentication and authorization

    failures (401/403) are returned with an empty body. The journey stats rollup

    adds `RESULT_SET_TOO_LARGE` (a **400**) when a filter matches more journeys

    than one request may cover; narrow it rather than paging further.


    **Rate limiting.** Requests are metered per tenant. Exceeding the budget

    yields **429** (empty body) on any endpoint; the `Retry-After` header gives

    the seconds to wait and `X-RateLimit-Limit` the sustained per-second budget.

    Product Activity has its own per-tenant budget in fixed-cost units rather
    than

    requests: 3,000 units per second with a burst of 15,000. A single-event
    request

    costs 1 unit and a batch request costs 2,000 units.
servers:
  - url: https://api.inflection.io
    description: Production
security:
  - PatAuth: []
tags:
  - name: Contacts
    description: Create, read, and update contacts. Writes are asynchronous.
  - name: Contact Activity
    description: >-
      Product, marketing, and combined activity for a contact, plus Salesforce
      data.
  - name: Journeys
    description: Read journeys (campaigns), their schedule and their derived status.
  - name: Stats
    description: >-
      Send and engagement metrics for journeys and emails, with a journey
      rollup.
  - name: Lists
    description: Static and dynamic lists, and static-list members.
  - name: Emails
    description: Email listing and creation.
  - name: Product Users
    description: |
      Read product users -- the logins underneath a contact -- by id or email,
      and list them for a contact or an organization.
  - name: Organizations
    description: Read organizations (company accounts) and a contact's memberships.
  - name: Email Versions
    description: Per-contact versions of a Personalized Email Asset, served under `/v1`.
  - name: Product Activity
    description: >
      Submit product events: actions your product users take in your product.


      **Accepted is not stored.** A `202` means an event passed validation and
      is

      durably queued. Each event name starts disabled: its events are archived
      but

      not stored or used by triggers until the name is enabled in Product Data

      Explorer, where it appears after its first event. Enabling takes effect
      within

      minutes and back-fills that event's recent history from the archive

      asynchronously; back-filled events do not fire triggers.


      **Identity.** `userId` is the product user id used by `POST /v2/users`,
      and

      `orgId` the organization id. Events never create or update product users
      or

      organizations; events for an id not created yet are kept and link up once
      it

      exists.


      **Deduplication and retries.** `eventId` identifies an event within your

      tenant. A repeat within the deduplication window is rejected with

      `DUPLICATE_EVENT`; later repeats are processed again. Always retry with
      the

      same `eventId`: resend events rejected with `DEPENDENCY_UNAVAILABLE`, or
      the

      whole request after a `500`; on a retry, `DUPLICATE_EVENT` normally means
      the

      event was already accepted. Valid events in one batch that share an
      `eventId` are all

      rejected.


      **Rate limit.** 3,000 units per second per tenant, with a burst of 15,000.
      A

      single-event request costs 1 unit and a batch request 2,000 units.
      Requests

      rejected after authentication also use the budget. On `429`,

      `X-RateLimit-Limit` is in units.
paths:
  /v2/product-events:
    post:
      tags:
        - Product Activity
      summary: Submit one product event
      description: >
        Validates, deduplicates and publishes one product event. Requires
        `WRITE`

        access for Personal Access Tokens and write-capable OAuth users.

        The request body is limited to 256 KiB of UTF-8 JSON. A `202` response
        means

        the event was accepted and durably queued.

        This request consumes 1 Product Activity rate-limit unit.
      operationId: createProductActivityEvent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProductActivityEvent'
            examples:
              event:
                $ref: '#/components/examples/ProductActivityEventRequest'
      responses:
        '202':
          $ref: '#/components/responses/ProductActivityAccepted'
        '400':
          $ref: '#/components/responses/ProductActivityBadRequest'
        '401':
          $ref: '#/components/responses/ProductActivityUnauthorized'
        '403':
          $ref: '#/components/responses/ProductActivityForbidden'
        '413':
          $ref: '#/components/responses/ProductActivityTooLarge'
        '415':
          $ref: '#/components/responses/ProductActivityUnsupportedMedia'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/ProductActivityInternalError'
        '503':
          $ref: '#/components/responses/ProductActivityUnavailable'
components:
  schemas:
    ProductActivityEvent:
      type: object
      additionalProperties: false
      x-max-encoded-bytes: 262144
      description: |
        One product event. The encoded event must not exceed 256 KiB. String
        limits count Unicode code points. See `timestamp` for the accepted
        formats.
      properties:
        eventId:
          type: string
          minLength: 1
          maxLength: 100
          description: >-
            Unique per event within your tenant; used for deduplication. Must
            not be blank.
        userId:
          type: string
          minLength: 1
          maxLength: 100
          description: The product user id, as used by `POST /v2/users`. Must not be blank.
        eventName:
          type: string
          minLength: 1
          maxLength: 200
          description: Must not be blank or start with `$`.
        orgId:
          type:
            - string
            - 'null'
          minLength: 1
          maxLength: 100
          description: >-
            Optional organization identifier; `null` is treated as absent and
            whitespace-only values are rejected.
        timestamp:
          description: >
            When the event happened, as one of:

            - An ISO 8601 date-time with an offset: `2026-11-04T10:52:00Z`,
              `2026-11-04T10:52:00.123+05:30`.
            - An ISO 8601 date-time without an offset, read as UTC:
              `2026-11-04T10:52:00`.
            - An integer number of Unix milliseconds: `1793789520000`. Seconds
              resolve to a date in 1970.

            Date-times need the `T` separator and at least hours and minutes;

            seconds and up to nine fraction digits are optional, and precision

            beyond microseconds is truncated. Offsets are `Z`, `±HH`, `±HH:MM`
            or

            `±HH:MM:SS` (`+0530` is rejected). A date alone, a space separator,
            a numeric string

            and a non-integer number are rejected. The result must fall in a UTC

            year from 1000 through 9999.
          oneOf:
            - type: string
            - type: integer
              format: int64
        properties:
          type: object
          minProperties: 1
          maxProperties: 256
          additionalProperties: true
          description: >
            Arbitrary JSON values. Top-level keys contain 1-200 Unicode code

            points and must not be blank; every string value, at any depth, is
            limited to 8,192 Unicode code

            points. Numbers must be within the finite IEEE 754 double-precision

            range. Nested objects and arrays are stored, but only top-level
            string,

            number and boolean values can be used in dynamic lists and triggers.
      required:
        - eventId
        - userId
        - eventName
        - timestamp
        - properties
    ProductActivityIngestionResponse:
      type: object
      additionalProperties: false
      description: >
        Contains only errors and metadata; accepted events are not listed
        individually.

        Missing required fields are grouped into one `INVALID_SCHEMA` error; a
        `null`

        value counts as missing. If none are missing, unknown fields are grouped
        the

        same way. Otherwise only the first validation failure is returned. Each

        failed event produces at most one public error.
      properties:
        errors:
          type: array
          items:
            $ref: '#/components/schemas/ProductActivityError'
        meta:
          $ref: '#/components/schemas/ProductActivityMeta'
      required:
        - meta
    ProductActivityError:
      type: object
      additionalProperties: false
      description: >
        Request-level errors may omit `paths`. Event-level batch paths begin
        with

        `/events/{originalIndex}` so indexes remain stable when other events
        fail;

        on the single-event endpoint paths have no prefix (for example
        `/eventId`).

        `MALFORMED_JSON`, `INVALID_SCHEMA`, `DUPLICATE_EVENT` and
        `INVALID_TENANT`

        produce `400` when no event is accepted. `TOO_LARGE` produces `413` when
        the

        request body is too large; an oversized event inside a batch is an event

        error instead (`400`, or `202` with `PARTIAL_SUCCESS`).

        `UNSUPPORTED_MEDIA_TYPE` and `UNSUPPORTED_CONTENT_ENCODING` produce
        `415`;

        `INTERNAL_ERROR` produces `500`; and `DEPENDENCY_UNAVAILABLE` produces
        `503`

        when no event is accepted, in which case other event errors may also be

        listed. A `202 PARTIAL_SUCCESS` may contain event errors.
      properties:
        errorCode:
          type: string
          enum:
            - INVALID_TENANT
            - UNSUPPORTED_MEDIA_TYPE
            - UNSUPPORTED_CONTENT_ENCODING
            - TOO_LARGE
            - MALFORMED_JSON
            - INVALID_SCHEMA
            - DUPLICATE_EVENT
            - DEPENDENCY_UNAVAILABLE
            - INTERNAL_ERROR
        message:
          type: string
        paths:
          type: array
          description: >-
            JSON Pointer locations. Batch event paths retain original event
            indexes.
          items:
            type: string
      required:
        - errorCode
        - message
    ProductActivityMeta:
      type: object
      additionalProperties: false
      properties:
        status:
          type: string
          enum:
            - SUCCESS
            - PARTIAL_SUCCESS
            - FAILURE
        timestamp:
          type: string
          format: date-time
        correlationId:
          type: string
          format: uuid
          description: Use this identifier when tracing a request with support.
      required:
        - status
        - timestamp
        - correlationId
  examples:
    ProductActivityEventRequest:
      summary: One product event
      value:
        eventId: event-123
        userId: user-42
        eventName: Viewed Page
        orgId: org-7
        timestamp: '2026-10-21T12:00:00Z'
        properties:
          page: /pricing
    ProductActivitySuccessResponse:
      summary: Single event accepted
      value:
        meta:
          status: SUCCESS
          timestamp: '2026-10-21T12:00:00.000Z'
          correlationId: 4d36e05b-21ab-4633-b811-cf4f1ad8f72c
    ProductActivityBatchSuccessResponse:
      summary: Every batch event accepted
      value:
        meta:
          status: SUCCESS
          timestamp: '2026-10-21T12:00:00.000Z'
          correlationId: 4d36e05b-21ab-4633-b811-cf4f1ad8f72c
    ProductActivityPartialSuccessResponse:
      summary: One batch event accepted and one rejected
      value:
        errors:
          - errorCode: INVALID_SCHEMA
            message: Required fields are missing.
            paths:
              - /events/1/userId
        meta:
          status: PARTIAL_SUCCESS
          timestamp: '2026-10-21T12:00:00.000Z'
          correlationId: 4d36e05b-21ab-4633-b811-cf4f1ad8f72c
    ProductActivityGroupedMissingFieldsResponse:
      summary: Missing fields grouped into one event error
      value:
        errors:
          - errorCode: INVALID_SCHEMA
            message: Required fields are missing.
            paths:
              - /events/0/eventId
              - /events/0/userId
        meta:
          status: FAILURE
          timestamp: '2026-10-21T12:00:00.000Z'
          correlationId: 4d36e05b-21ab-4633-b811-cf4f1ad8f72c
    ProductActivityDuplicateResponse:
      summary: Duplicate event rejected
      value:
        errors:
          - errorCode: DUPLICATE_EVENT
            message: Event is a duplicate.
        meta:
          status: FAILURE
          timestamp: '2026-10-21T12:00:00.000Z'
          correlationId: 4d36e05b-21ab-4633-b811-cf4f1ad8f72c
    ProductActivityTooLargeResponse:
      summary: Request body limit exceeded
      value:
        errors:
          - errorCode: TOO_LARGE
            message: Request body must not exceed 262144 UTF-8 bytes.
        meta:
          status: FAILURE
          timestamp: '2026-10-21T12:00:00.000Z'
          correlationId: 4d36e05b-21ab-4633-b811-cf4f1ad8f72c
    ProductActivityUnavailableResponse:
      summary: Required dependency unavailable
      value:
        errors:
          - errorCode: DEPENDENCY_UNAVAILABLE
            message: >-
              Event could not be processed because a required dependency is
              unavailable.
        meta:
          status: FAILURE
          timestamp: '2026-10-21T12:00:00.000Z'
          correlationId: 4d36e05b-21ab-4633-b811-cf4f1ad8f72c
  responses:
    ProductActivityAccepted:
      description: >
        `SUCCESS`: every event was accepted. `PARTIAL_SUCCESS` (batch only): at
        least

        one event was accepted and the rejected ones are listed in `errors`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ProductActivityIngestionResponse'
          examples:
            success:
              $ref: '#/components/examples/ProductActivitySuccessResponse'
            batchSuccess:
              $ref: '#/components/examples/ProductActivityBatchSuccessResponse'
            partialSuccess:
              $ref: '#/components/examples/ProductActivityPartialSuccessResponse'
    ProductActivityBadRequest:
      description: >
        No event was accepted: malformed JSON (including an empty body, invalid
        UTF-8, a NUL

        byte, duplicate keys, trailing content or excessive nesting), an invalid

        schema, too many events in a batch, or every event invalid, oversized or
        a

        duplicate.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ProductActivityIngestionResponse'
          examples:
            groupedMissingFields:
              $ref: >-
                #/components/examples/ProductActivityGroupedMissingFieldsResponse
            duplicate:
              $ref: '#/components/examples/ProductActivityDuplicateResponse'
    ProductActivityUnauthorized:
      description: >-
        Missing or invalid Personal Access Token or OAuth access token. Empty
        body.
    ProductActivityForbidden:
      description: The credential lacks `WRITE` access. Empty body.
    ProductActivityTooLarge:
      description: The request body exceeds its endpoint limit.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ProductActivityIngestionResponse'
          examples:
            tooLarge:
              $ref: '#/components/examples/ProductActivityTooLargeResponse'
    ProductActivityUnsupportedMedia:
      description: |
        `Content-Type` must be `application/json`, optionally with
        `charset=utf-8`. Compressed bodies (any `Content-Encoding` other than
        `identity`) are not accepted.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ProductActivityIngestionResponse'
    TooManyRequests:
      description: |
        Per-tenant rate limit exceeded. Empty body. Retry after the number of
        seconds given in the `Retry-After` header.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
        X-RateLimit-Limit:
          description: Sustained request budget, in requests per second, per tenant.
          schema:
            type: integer
    ProductActivityInternalError:
      description: |
        The request could not be processed. Some events may already have been
        accepted; resend the whole request unchanged.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ProductActivityIngestionResponse'
    ProductActivityUnavailable:
      description: >
        No event was accepted because a required dependency was unavailable.
        Resend

        the events whose error is `DEPENDENCY_UNAVAILABLE`, unchanged; any other

        events listed in `errors` were invalid, oversized or duplicates. A `503`
        with

        an empty body means authentication was temporarily unavailable.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ProductActivityIngestionResponse'
          examples:
            unavailable:
              $ref: '#/components/examples/ProductActivityUnavailableResponse'
  securitySchemes:
    PatAuth:
      type: http
      scheme: bearer
      bearerFormat: inf_pat
      description: |
        Personal Access Token or OAuth 2.1 access token, sent as a bearer
        credential (`Authorization: Bearer inf_pat_...`). Read operations
        require a token with the `READ` scope; write operations
        (POST/PATCH/DELETE) require `WRITE`.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.