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

# Create a comment

> Posts a comment on a partner's timeline: the same comment your team and partners see in the partner portal, the CRM embed, and email/Slack notifications. This is the in-app comment composer over the API: plain-text, Markdown, or HTML comments (HTML is converted server-side), `@Name` / `@email` mentions (or an explicit `mentions` array), internal (org-only) visibility, and thread replies.

**Where the comment lands** is resolved from the targets you pass:
- `partnerId`: the partner portal timeline. Accepts the Introw partner id or the partner's CRM external id.
- `crmObjectId` + `crmObjectType`: a deal, ticket, or other CRM object. Pass `crmObjectType` in your own CRM's wording (`Opportunity` or `Deal`, whichever your CRM uses). See [CRM object types](#crm-object-types). The partner is resolved from the object's partner attribution (or pass `partnerId` explicitly). The comment is also synced as a note to your CRM.
- `taskId`: a partner portal task.
- `formSubmissionId`: a form submission (deal registration, shared lead, MDF request, …).
- `commissionPayoutId`: a commission payout. Comments on payouts the partner cannot see yet are automatically kept internal.
- `threadId`: reply to an existing comment (use the `threadId` from the create response).

**Who it is posted as**: `authorEmail` must match an organisation team member or a partner portal member of the target partner. Only team-member authors may set `isInternal: true`. Internal comments stay hidden from partner-facing surfaces and never trigger partner notifications.

Notifications, workflow triggers, and CRM note sync behave exactly as if the comment was posted in the app.

### CRM object types

`crmObjectType` takes your CRM's own name for the object, case-insensitively. Introw translates it, so there is nothing to look up or map on your side:

- **Deal**: Salesforce `Opportunity`, HubSpot `Deal`
- **Company**: Salesforce `Account`, HubSpot `Company`
- **Ticket**: Salesforce `Case`, HubSpot `Ticket`
- **Contact**: `Contact` in both
- **Lead**: `Lead` in both

Introw's own type names (`DEAL`, `COMPANY`, `TICKET`, `CONTACT`, `LEAD`) and HubSpot object type ids (`0-3`, `0-2`, `0-5`, `0-1`, `0-136`) resolve to the same objects.

**Custom objects** are the one case where the exact name matters: pass the object's API name as your CRM shows it, for example `Partner_Program__c` in Salesforce or `p_partner_application` / `2-12345` in HubSpot. Custom types are used verbatim, not translated.

### More payloads

Comment on a deal (the partner is resolved from the deal's attribution):

```bash
curl -X POST "https://api.introw.io/api/v1/comments" \
  -H "x-api-key: $INTROW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "comment": "Pricing approved on our side, over to you for the paperwork.",
    "authorEmail": "alex@vendor.example",
    "crmObjectId": "9840193344",
    "crmObjectType": "DEAL",
    "isInternal": false
  }'
```

Reply in an existing thread:

```bash
curl -X POST "https://api.introw.io/api/v1/comments" \
  -H "x-api-key: $INTROW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "comment": "Signed order form is in the deal room, **we are good to go**.",
    "authorEmail": "jamie@partner.example",
    "formSubmissionId": "fsub_01HVK6Y8Z8Q7J8J8J8J8J8J8J8",
    "threadId": "cmt_01HVK6Y8Z8Q7J8J8J8J8J8J8J8"
  }'
```

Mention people with `@Name` / `@email` in the body and/or an explicit `mentions` array:

```bash
curl -X POST "https://api.introw.io/api/v1/comments" \
  -H "x-api-key: $INTROW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "comment": "Can you take a look at the pricing on this deal @Alex?",
    "mentions": [
      "jamie@partner.example"
    ],
    "authorEmail": "alex@vendor.example",
    "crmObjectId": "9840193344",
    "crmObjectType": "DEAL"
  }'
```



## OpenAPI

````yaml /openapi.json post /api/v1/comments
openapi: 3.1.0
info:
  title: Introw API
  version: '2026-05-20'
  description: >-
    Customer-facing API for Introw. Manage partners, delegate commission payout
    operations to your finance software, embed the partner portal in your
    product with pre-authenticated sessions, ingest affiliate conversions, and
    trigger on-demand CRM object syncs.
servers:
  - url: https://api.introw.io
  - url: https://api.staging.introw.io
security:
  - ApiKeyAuth: []
tags:
  - name: Partners
    description: >-
      Read and manage partners for the organisation associated with the
      authenticated API key.
  - name: Payouts
    description: >-
      Read and update commission payouts. Delegate approval, scheduling, and
      payment status to your ERP or accounts-payable stack.
  - name: Commission lines
    description: >-
      Create, read, update, decline, and detach commission line items. Lines can
      live in the pending pool or be attached to a payout.
  - name: Collaboration
    description: >-
      Post comments on partner portals, deals, tasks, form submissions, and
      payouts, with rich text, internal visibility, and thread replies. Comments
      trigger the same notifications, workflows, and CRM note sync as comments
      posted in the app.
  - name: Forms
    description: >-
      Submit Introw forms server-to-server. Register deals, share leads, or file
      any other form submission from your own systems; submissions run the exact
      same automation and acceptance pipeline as portal submissions. Read a
      form's field schema first to discover the field ids, value shapes, and
      allowed values to send.
  - name: Auth
    description: >-
      Create pre-authenticated partner portal sessions. Exchange a visitor email
      for a short-lived, single-use portal URL that you embed in an iframe
      inside your own product: one login, fully branded, no login screen for the
      partner.
  - name: Affiliate
    description: >-
      Ingest affiliate conversions from your backend or directly from the
      browser via affiliate.js. Every conversion is anchored to a signed,
      server-side click token (the first-party `_introw_aff` cookie),
      deduplicated, and mapped onto a campaign form submission, so attribution
      cannot be forged or double-counted.
  - name: CRM
    description: >-
      Trigger on-demand syncs of individual CRM objects. Introw already syncs
      every object automatically about every 15 minutes; use this only when a
      change must be reflected in Introw immediately, e.g. a workflow mutated
      the record and its result must persist in Introw right away.
paths:
  /api/v1/comments:
    post:
      tags:
        - Collaboration
      summary: Create a comment
      description: >-
        Posts a comment on a partner's timeline: the same comment your team and
        partners see in the partner portal, the CRM embed, and email/Slack
        notifications. This is the in-app comment composer over the API:
        plain-text, Markdown, or HTML comments (HTML is converted server-side),
        `@Name` / `@email` mentions (or an explicit `mentions` array), internal
        (org-only) visibility, and thread replies.


        **Where the comment lands** is resolved from the targets you pass:

        - `partnerId`: the partner portal timeline. Accepts the Introw partner
        id or the partner's CRM external id.

        - `crmObjectId` + `crmObjectType`: a deal, ticket, or other CRM object.
        Pass `crmObjectType` in your own CRM's wording (`Opportunity` or `Deal`,
        whichever your CRM uses). See [CRM object types](#crm-object-types). The
        partner is resolved from the object's partner attribution (or pass
        `partnerId` explicitly). The comment is also synced as a note to your
        CRM.

        - `taskId`: a partner portal task.

        - `formSubmissionId`: a form submission (deal registration, shared lead,
        MDF request, …).

        - `commissionPayoutId`: a commission payout. Comments on payouts the
        partner cannot see yet are automatically kept internal.

        - `threadId`: reply to an existing comment (use the `threadId` from the
        create response).


        **Who it is posted as**: `authorEmail` must match an organisation team
        member or a partner portal member of the target partner. Only
        team-member authors may set `isInternal: true`. Internal comments stay
        hidden from partner-facing surfaces and never trigger partner
        notifications.


        Notifications, workflow triggers, and CRM note sync behave exactly as if
        the comment was posted in the app.


        ### CRM object types


        `crmObjectType` takes your CRM's own name for the object,
        case-insensitively. Introw translates it, so there is nothing to look up
        or map on your side:


        - **Deal**: Salesforce `Opportunity`, HubSpot `Deal`

        - **Company**: Salesforce `Account`, HubSpot `Company`

        - **Ticket**: Salesforce `Case`, HubSpot `Ticket`

        - **Contact**: `Contact` in both

        - **Lead**: `Lead` in both


        Introw's own type names (`DEAL`, `COMPANY`, `TICKET`, `CONTACT`, `LEAD`)
        and HubSpot object type ids (`0-3`, `0-2`, `0-5`, `0-1`, `0-136`)
        resolve to the same objects.


        **Custom objects** are the one case where the exact name matters: pass
        the object's API name as your CRM shows it, for example
        `Partner_Program__c` in Salesforce or `p_partner_application` /
        `2-12345` in HubSpot. Custom types are used verbatim, not translated.


        ### More payloads


        Comment on a deal (the partner is resolved from the deal's attribution):


        ```bash

        curl -X POST "https://api.introw.io/api/v1/comments" \
          -H "x-api-key: $INTROW_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "comment": "Pricing approved on our side, over to you for the paperwork.",
            "authorEmail": "alex@vendor.example",
            "crmObjectId": "9840193344",
            "crmObjectType": "DEAL",
            "isInternal": false
          }'
        ```


        Reply in an existing thread:


        ```bash

        curl -X POST "https://api.introw.io/api/v1/comments" \
          -H "x-api-key: $INTROW_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "comment": "Signed order form is in the deal room, **we are good to go**.",
            "authorEmail": "jamie@partner.example",
            "formSubmissionId": "fsub_01HVK6Y8Z8Q7J8J8J8J8J8J8J8",
            "threadId": "cmt_01HVK6Y8Z8Q7J8J8J8J8J8J8J8"
          }'
        ```


        Mention people with `@Name` / `@email` in the body and/or an explicit
        `mentions` array:


        ```bash

        curl -X POST "https://api.introw.io/api/v1/comments" \
          -H "x-api-key: $INTROW_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "comment": "Can you take a look at the pricing on this deal @Alex?",
            "mentions": [
              "jamie@partner.example"
            ],
            "authorEmail": "alex@vendor.example",
            "crmObjectId": "9840193344",
            "crmObjectType": "DEAL"
          }'
        ```
      operationId: createComment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCommentBody'
            example:
              comment: >-
                Thanks for the intro call, sending the co-sell deck over later
                today.
              authorEmail: alex@vendor.example
              partnerId: ptn_01HVK6Y8Z8Q7J8J8J8J8J8J8J8
      responses:
        '201':
          description: Comment created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateCommentResponse'
              example:
                data:
                  id: '48291'
                  threadId: cmt_01HVK6Y8Z8Q7J8J8J8J8J8J8J8
                  partnerId: ptn_01HVK6Y8Z8Q7J8J8J8J8J8J8J8
                  isInternal: false
                  createdAt: '2026-07-29T12:00:00.000Z'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '401':
          description: API key is missing, invalid, expired, or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '402':
          description: >-
            Monthly API credit allowance reached. Credits reset at the start of
            the next calendar month.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
            retry-after:
              description: >-
                Seconds until the API credit allowance resets (the start of the
                next calendar month).
              schema:
                type: string
        '403':
          description: API key does not include the collaborate:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '404':
          description: >-
            A referenced target (partner, task, form submission, CRM object,
            payout, or thread) was not found in the authenticated organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '422':
          description: >-
            Request body failed validation, the author could not be resolved, a
            mention could not be resolved (or was ambiguous), the CRM object is
            not linked to a partner, or a portal-member author attempted an
            internal comment.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '429':
          description: Rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '500':
          description: Unexpected server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
        '502':
          description: Comment creation failed. Retry later.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          headers:
            x-ratelimit-limit-minute:
              description: Maximum number of requests allowed in the current minute.
              schema:
                type: string
            x-ratelimit-remaining-minute:
              description: Number of requests remaining in the current minute.
              schema:
                type: string
            x-introw-credits-limit-month:
              description: >-
                This month's API credit allowance. Absent when the request was
                not metered (for example an authentication failure) or when the
                allowance is unlimited.
              schema:
                type: string
            x-introw-credits-remaining-month:
              description: >-
                API credits left in the current calendar month, after this
                request. `0` on a `402`. Absent when the request was not metered
                or when the allowance is unlimited.
              schema:
                type: string
components:
  schemas:
    CreateCommentBody:
      type: object
      properties:
        comment:
          type: string
          minLength: 1
          maxLength: 20000
          description: >-
            Comment body as plain text or Markdown (`**bold**`, `*italic*`,
            `[links](url)`, blank-line paragraphs, and `-` / `1.` lists). HTML
            fragments are also accepted and converted to the same TipTap format.
            Use `@Name` or `@email` to mention people; unresolved or ambiguous
            mentions are rejected with a short list of candidates. Formatting is
            preserved on the partner portal timeline.
        mentions:
          description: >-
            People to @-mention on the comment, in addition to any `@Name` /
            `@email` tokens in `comment`. Each entry is an email or display name
            resolved against organisation team members and active partner portal
            members. Resolved mentions are appended to the comment and trigger
            mention notifications.
          maxItems: 20
          type: array
          items:
            type: string
            minLength: 1
            maxLength: 255
        authorEmail:
          type: string
          format: email
          pattern: >-
            ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
          description: >-
            Email of the person the comment is posted as. Must match an
            organisation team member or a partner portal member in the API key's
            organisation.
        isInternal:
          description: >-
            When true, the comment stays hidden from partner-facing surfaces and
            never triggers partner notifications. Only allowed when
            `authorEmail` is an organisation team member.
          type: boolean
        partnerId:
          description: >-
            Partner to comment on (partner portal timeline). Accepts the Introw
            partner id or the partner's CRM external id.
          type: string
          minLength: 1
        taskId:
          description: Partner portal task to comment on.
          type: integer
          exclusiveMinimum: 0
          maximum: 9007199254740991
        formSubmissionId:
          description: >-
            Form submission (deal registration, shared lead, MDF request, …) to
            comment on.
          type: string
          minLength: 1
        commissionPayoutId:
          description: >-
            Commission payout to comment on. Comments on payouts that are not
            yet visible to the partner are automatically kept internal.
          type: string
          minLength: 1
        threadId:
          description: >-
            Thread to reply in. Use the `threadId` returned when the parent
            comment was created.
          type: string
          minLength: 1
        crmObjectId:
          description: >-
            External CRM identifier of the object (deal, ticket, …) to comment
            on. Requires `crmObjectType`.
          type: string
          minLength: 1
        crmObjectType:
          description: >-
            Type of the object in `crmObjectId`, in your own CRM's wording.
            Introw translates it: Salesforce `Opportunity`, `Account`, `Case`,
            `Contact`, `Lead` and HubSpot `Deal`, `Company`, `Ticket`,
            `Contact`, `Lead` are all accepted (case-insensitive), as are
            Introw's own `DEAL`, `COMPANY`, `TICKET`, `CONTACT`, `LEAD`. For a
            custom object, pass its API name exactly as your CRM shows it (for
            example `Partner_Program__c` or `p_partner_application`). The object
            must be attributed to a partner (or pass `partnerId` explicitly).
          type: string
          minLength: 1
      required:
        - comment
        - authorEmail
      additionalProperties: false
    CreateCommentResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            id:
              type: string
              description: Comment identifier.
            threadId:
              type: string
              description: >-
                Thread identifier. Pass it as `threadId` to reply to this
                comment.
            partnerId:
              anyOf:
                - type: string
                - type: 'null'
              description: Partner whose timeline the comment landed on.
            isInternal:
              type: boolean
              description: Whether the comment is hidden from partner-facing surfaces.
            createdAt:
              type: string
              format: date-time
              pattern: >-
                ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
              description: ISO 8601 timestamp for when the comment was created.
          required:
            - id
            - threadId
            - partnerId
            - isInternal
            - createdAt
          additionalProperties: false
      required:
        - data
      additionalProperties: false
    PublicApiError:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: Stable machine-readable error code.
            message:
              type: string
              description: Human-readable error message.
            details:
              description: Optional structured details for validation and debugging.
          required:
            - code
            - message
          additionalProperties: false
      required:
        - error
      additionalProperties: false
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: Introw API key shown once when the credential is created.

````