> ## 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.

# Comments & nudges

> Post a comment on any partner, deal, task, submission, or payout from your own systems - and reach the partner on the channel they already use.

A comment in Introw is not a portal message. It lands on the partner's timeline, attaches to the record it is about, and reaches the partner through whichever channel they already use - email, Slack, Microsoft Teams, or their AI assistant. The [Create a comment](/api-reference/collaboration/create-a-comment) endpoint puts that whole path behind one call.

That makes it the API you reach for when something in your stack knows a partner should act, and nobody has time to chase them by hand.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST "https://api.introw.io/api/v1/comments" \
  -H "x-api-key: $INTROW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "comment": "This deal has been in **Proposal** for 21 days. Can you confirm the next step?",
    "authorEmail": "alex@vendor.example",
    "crmObjectId": "9840193344",
    "crmObjectType": "DEAL"
  }'
```

## Why this endpoint exists

Partner programs stall in the gap between *knowing* a partner should act and *getting them to act*. The signal usually lives in a system that is not Introw: a stage-age query, a churn model, a certification expiry, a finance run, an agent working the pipeline. The nudge has to happen in a place the partner actually reads.

* **Nudge at scale, on a real signal.** Run your own rules - deal age, missing next step, unsigned order form, lapsed certification, dormant partner - and post one comment per hit. Ten thousand partners is ten thousand calls, not ten thousand emails somebody wrote.
* **Let an agent do the chasing.** An agent that reviews your pipeline can post the follow-up itself, as a named person, with a human reading the same thread. This is the [AI-native](/why/ai-native) path: the judgment is automated, the accountability is not.
* **Close the loop from your own systems.** Your billing system comments on a payout when it is scheduled. Your provisioning system comments on the deal when the account goes live. The partner hears it from the record, not from a separate status email.
* **Acknowledge intake instantly.** Right after [submitting a form](/general/forms-overview), comment on the submission with what happens next and by when. Partners stop asking whether it arrived.
* **Annotate for your own team.** Set `isInternal` and the comment stays on the record for your team, invisible to the partner and silent on every partner-facing surface.

Because the comment lands on the record rather than in an inbox, the conversation stays where the work is - and the partner replies in the same thread from wherever they read it.

## Choose a target

Every comment lands on a partner's timeline. What you additionally target decides which record it hangs off. Pass at least one:

| Field                           | Comments on                                                                                          |
| ------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `partnerId`                     | The partner's timeline directly. Accepts the Introw partner id **or** the partner's CRM external id. |
| `crmObjectId` + `crmObjectType` | A CRM record: a deal, ticket, lead, company, contact, or custom object.                              |
| `taskId`                        | A partner portal task.                                                                               |
| `formSubmissionId`              | A deal registration, shared lead, MDF request, or any other submission.                              |
| `commissionPayoutId`            | A commission payout.                                                                                 |
| `threadId`                      | A reply in an existing thread.                                                                       |

Two rules the API enforces for you:

* `crmObjectId` and `crmObjectType` must be sent together, and the object must already be attributed to a partner - otherwise pass `partnerId` explicitly.
* The collaboration space the comment lands in is resolved server-side. You never pass a room, and you never have to know one exists.

### Name the object type however your CRM does

`crmObjectType` takes your CRM's own word for the object, case-insensitively. Introw translates it, so you never map types on your side:

| Object  | Salesforce    | HubSpot   |
| ------- | ------------- | --------- |
| Deal    | `Opportunity` | `Deal`    |
| Company | `Account`     | `Company` |
| Ticket  | `Case`        | `Ticket`  |
| Contact | `Contact`     | `Contact` |
| Lead    | `Lead`        | `Lead`    |

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

**Custom objects** are the one case where the exact name matters. Pass the API name as your CRM shows it, for example `Partner_Program__c` in Salesforce or `p_partner_application` in HubSpot. A custom type has no standard equivalent, so it is used verbatim rather than translated.

## Post as a real person

`authorEmail` decides who the comment appears to be from, and it must match a real person: either one of your team members, or a portal member of the partner the comment lands on. Comments are attributed, not posted by "the system", so a partner sees their partner manager following up rather than an unsigned automation.

This is also what governs internal comments: `isInternal` is only allowed when `authorEmail` is one of **your** team members. A partner-authored comment can never be internal.

<Note>
  A comment on a commission payout that is not yet visible to the partner is kept internal
  automatically, whatever you send. Payout figures never leak ahead of the payout being released.
</Note>

## Write the comment

`comment` takes plain text or Markdown, and formatting survives onto the partner portal timeline:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "comment": "Two things before Friday:\n\n- Confirm the **economic buyer**\n- Attach the signed [order form](https://example.com/of)\n\nShout if the timeline slipped."
}
```

Bold, italic, links, blank-line paragraphs, and `-` / `1.` lists all render. HTML fragments are accepted too and converted to the same format, so you can forward content you already have. Comments are capped at 20,000 characters, and the endpoint does not take attachments.

## What comes back

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "data": {
    "id": "48291",
    "threadId": "cmt_01HVK6Y8Z8Q7J8J8J8J8J8J8J8",
    "partnerId": "ptn_01HVK6Y8Z8Q7J8J8J8J8J8J8J8",
    "isInternal": false,
    "createdAt": "2026-07-29T12:00:00.000Z"
  }
}
```

Keep `threadId` if you intend to follow up - passing it back as `threadId` puts the next comment in the same thread instead of starting a new one. Check `isInternal` on the response rather than assuming: it tells you what the comment actually became after the payout rule above was applied.

## Notifications

A comment that is not internal notifies the partner through the channels you have configured for your program - email, Slack, Microsoft Teams - following the same notification rules and preferences as a comment your team posts in the app. An internal comment notifies nobody on the partner side.

That is what makes this a nudge API rather than a logging API: you are not writing a record, you are reaching a person. Treat volume accordingly - see [Control who gets notified](/features/engagement/notifications/guides/control-who-gets-notified).

## Errors

| HTTP  | Code                                                                                                              | What happened                                                                        |
| ----- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| `403` | `FORBIDDEN`                                                                                                       | The key lacks `collaborate:write`.                                                   |
| `404` | `PARTNER_NOT_FOUND`                                                                                               | `partnerId` matched neither an Introw partner id nor a CRM external id.              |
| `404` | `CRM_OBJECT_NOT_FOUND` / `TASK_NOT_FOUND` / `FORM_SUBMISSION_NOT_FOUND` / `PAYOUT_NOT_FOUND` / `THREAD_NOT_FOUND` | The target does not exist in this organisation.                                      |
| `422` | `VALIDATION_ERROR`                                                                                                | No target was provided, or `crmObjectId` and `crmObjectType` were not sent together. |
| `422` | `AUTHOR_NOT_FOUND`                                                                                                | `authorEmail` matches no team member, and no portal member of the comment's partner. |
| `422` | `INTERNAL_AUTHOR_FORBIDDEN`                                                                                       | `isInternal` was set but `authorEmail` is not one of your team members.              |
| `422` | `CRM_OBJECT_NOT_LINKED`                                                                                           | The CRM object is not attributed to a partner. Pass `partnerId` as well.             |
| `422` | `COMMENT_TARGET_UNRESOLVED`                                                                                       | The targets you sent resolved to no partner to comment on.                           |
| `502` | `COMMENT_CREATE_FAILED`                                                                                           | The comment could not be written. Safe to retry.                                     |

## Authentication

Use a secret API key with the `collaborate:write` scope - see [Authentication](/general/authentication).

## API reference

<CardGroup cols={1}>
  <Card title="Create a comment" icon="comment" href="/api-reference/collaboration/create-a-comment">
    POST /api/v1/comments
  </Card>
</CardGroup>

## Related

<CardGroup cols={2}>
  <Card title="Nudge partners at scale via the API" icon="book-open" href="/features/developer/api/guides/nudge-partners-at-scale-via-the-api">
    The step-by-step guide, with the patterns worth copying.
  </Card>

  <Card title="Form submissions" icon="paper-plane" href="/general/forms-overview">
    Create the submission you are commenting on.
  </Card>

  <Card title="Collaborate on a shared deal" icon="handshake" href="/features/co-selling/shared-pipelines/guides/collaborate-on-a-shared-deal">
    The same thread, worked by hand in the app.
  </Card>

  <Card title="Nudge stalled deals" icon="bell" href="/features/engagement/notifications/guides/nudge-stalled-deals">
    The no-code version, configured on a pipeline.
  </Card>
</CardGroup>
