openapi: 3.2.0 info: title: Inbox 2.0 API Reference CRM Webhooks API description: Inbox 2.0 API Reference version: v1 x-logo: url: static/hootsuite-logo.png contact: email: dev.support@hootsuite.com license: name: Hootsuite Developer Terms and API License Agreement url: https://hootsuite.com/legal/dev-api-terms servers: - url: https://platform.hootsuite.com description: Inbox 2.0 production server security: - bearer-token: [] tags: - name: crm_webhooks x-displayName: Webhooks description: "### Webhook authentication\n\nWhen receiving data from Inbox 2.0, we provide two authentication options. Both mechanisms are in place so that you can make sure the request originates from Inbox 2.0.\n\n#### Shared secret\n\nIf you choose the shared secret authentication method, a secret will be generated for you. This secret allows you to calculate the signature to verify that the call originated from Inbox 2.0. With this mechanism, every single request from Inbox 2.0 contains the `X-Hootsuite-Signature` header. Here's an example of a request:\n\n```shell\ncurl -X POST https://my-webhook-url \\\n -H 'content-type : application/json' \\\n -H 'accept: application/json' \\\n -H 'X-Hootsuite-Signature: e6f93239a06e46ae9654fc9ad2fb4e1cc4eb213830a0d94e711570c047e43c57' \\\n -d '{\n \"version\": 2,\n \"contactProfile\": {\n \"id\": \"a7a20053-9c54-11eb-a89f-47717a44c639\"\n },\n \"contactAttributes\": [\n {\"attribute\": \"email\", \"value\": \"fj@example.com\"}\n ]\n }'\n```\n\nThe signature is generated using the `HMAC-SHA256` algorithm with the shared secret and the request body. \nUse your secret to calculate the signature and compare with the given signature. \nBoth the secret key you received and the signature are encoded as hexadecimal strings. \nMake sure to convert the shared secret from its hexadecimal representation to its binary format before using it. \nMost languages come with libraries out of the box to verify this signature. \nFor example, here's how it looks in JavaScript:\n\n```javascript\nconst crypto = require(\"crypto\");\nconst secret = \"...\"; // do not share!\nconst expectedSignature = request.headers[\"X-Hootsuite-Signature\"];\nconst actualSignature = crypto\n .createHmac(\"sha256\", Buffer.from(secret, \"hex\"))\n .update(request.body, \"utf-8\")\n .digest(\"hex\");\nif (actualSignature !== expectedSignature) {\n throw new createError.Unauthorized(\"X-Hootsuite-Signature wrong\");\n}\nconsole.log(JSON.parse(request.body).email);\n```\n\n#### OAuth\n\nIf your endpoints support OAuth2, you can configure your client credentials, a Token URL, and, optionally, a Scope in Inbox 2.0.\n\nWe use the OAuth2 Client Credentials flow to authenticate against your CRM. The Token URL is the endpoint where we can authenticate with these credentials and retrieve an access token. To do the actual lookup, write back, or notification requests, we use the token in the Authorization Header to authenticate.\n" paths: {} webhooks: crm-attribute-lookup: post: summary: Contact attribute lookup description: "Inbox 2.0 CRM integration allows you to pull customer contact data from CRMs or other internal business applications into Inbox 2.0. To integrate your CRM with Inbox 2.0, you need to provide an HTTPS endpoint for lookups.\n\n### Create contact attributes\n\n1. In Inbox 2.0, go to `Admin settings`, expand `Agent Workspace`, select `Contact attributes`, and then select `Add attribute`.\n2. Select `Managed by CRM`. The CRM is the source of truth for these attributes. After they are imported into Inbox 2.0, they only change when the value changes in the CRM.\n3. Identify lookup attributes by selecting `Use as lookup attribute`. These are used by the CRM to find a customer's data. For example, if you want to find a customer by email address, create an attribute called \"Email\" and designate it as a lookup. When the request is made from Inbox 2.0 to your CRM, the lookup fields are included in the request body.\n4. In the `Unique identifier` box, enter an alias for the lookup attribute. This is used to map the response from your system to the attribute.\n\n### Configure integration URL\n\nTo pull data from your CRM into Inbox 2.0, you need to implement a POST endpoint that accepts a JSON request and returns a JSON response. The request contains the lookup attributes, and the response should contain the contact data from your CRM.\n\nAfter the endpoint is implemented, configure it as the lookup URL:\n\n1. Go to `Admin settings`.\n2. Expand `Integration and APIs`.\n3. Select `CRM`.\n\n### Handle the request\n\nWhen the endpoint in your system is ready and configured in Inbox 2.0, and one or more lookup attributes have been identified, you can make a request via the `Lookup` button in the conversation view.\nThis will trigger the CRM attribute lookup event.\n\nAfter you've done the lookup in your system, pass attributes back to Inbox 2.0 as a JSON response. The keys in the attributes object correspond to the aliases that were previously added in Inbox 2.0. If a CRM managed attribute is missing from the object or the key has a `null` value, its value is deleted in Inbox 2.0. Inbox 2.0 expects the response in this format:\n\n```json\n{\n \"autoConfirm\": false,\n \"contactAttributes\": [\n {\"attribute\": \"email\", \"value\": \"fj@example.com\"},\n {\"attribute\": \"id\", \"value\": \"98765432\"},\n {\"attribute\": \"first_name\", \"value\": \"Fred\"},\n {\"attribute\": \"last_name\", \"value\": \"Jones\"},\n {\"attribute\": \"last_order_number\", null}\n ]\n}\n```\n\n### Asynchronous Response\nYou can also return a 200 OK back without a body and call our REST api to send back the result of the lookup request.\nCall the following REST endpoint:\n\n```bash\ncurl --request PUT 'https://platform.hootsuite.com/inbox/v2/contact/{contactProfileId}/contact-attributes' \\\n--header 'content-type: application/json' \\\n--header 'Authorization: BEARER ' \\\n--data-raw '{\n \"autoConfirm\": false,\n \"contactAttributes\": [\n {\"attribute\": \"email\", \"value\": \"fj@example.com\"},\n {\"attribute\": \"id\", \"value\": \"98765432\"},\n {\"attribute\": \"first_name\", \"value\": \"Fred\"},\n {\"attribute\": \"last_name\", \"value\": \"Jones\"},\n {\"attribute\": \"last_order_number\", null}\n ]\n }'\n```\n\n### Confirm your attributes\n\nThe CRM attributes sent back in the response must be confirmed before they are saved in Inbox 2.0. There are two ways to do that:\n\n - Select the `Confirm` button when you see the CRM attributes displayed in Inbox 2.0 after the lookup request.\n - Add an `autoConfirm` field to the response.\n\nThe `autoConfirm` field tells Inbox 2.0 to automatically save the attributes without having to use the `Confirm` button.\n" operationId: crmAttributeLookup security: - Oauth2ClientCredentials: [] - SharedSecret: [] tags: - crm_webhooks requestBody: required: true content: application/json: schema: type: object description: JSON object containing the lookup attributes additionalProperties: type: string example: my-customer-id: '123' responses: '200': description: 'Returns the attributes to be updated in Inbox 2.0. ' content: application/json: schema: $ref: '#/components/schemas/CrmLookupAttributesResponse' 4xx: description: 'Lookup failed ' 5xx: description: 'Lookup failed ' crm-error-notifications: post: summary: Contact attribute lookup error notifications event description: "When attribute update validation errors occur, Inbox 2.0 sends a request to the configured notification URL.\n\n### Configure notification URL\n\nWhen you send your CRM managed contact attributes, Inbox 2.0 does some validation on the response. To see validation errors, we've provided a way to send notifications from Inbox 2.0 to your system. You need to provide a POST endpoint that accepts a JSON request body. Configure a notification URL on the same screen where you configured a lookup URL:\n\n 1. Go to `Admin settings`.\n 2. Expand `Integration and APIs`.\n 3. Select `CRM`.\n\n### Handle the request\n\nWhen validation errors occur, Inbox 2.0 sends a request to the configured notification URL. A request [looks like this](#operation/crmAttributeLookupValidationErrorNotification).\n\nThe request contains these two fields:\n\n - eventType\n - messages: an array of explicit error messages\n\nThe following are the event types that can be sent by Inbox 2.0.\n\n| Event type | Description |\n|-------------------|-------------------------------------------------------------|\n| DATA_IMPORT_ERROR | Error occurred while pulling data from the CRM to Inbox 2.0. |\n\nThe response to this request can be a 204. Inbox 2.0 does not expect anything in the response body.\n" operationId: crmContactAttributeLookupErrorNotification security: - Oauth2ClientCredentials: [] - SharedSecret: [] tags: - crm_webhooks requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CrmErrorNotificationsRequest' responses: '204': description: 'To indicate that the notification was received successfully ' 4xx: description: 'Could not process notification ' 5xx: description: 'Could not process notification ' crm-write-back: post: summary: 'CRM write back event ' description: "# CRM write back\n\nYou can be notified whenever a conversation has taken place in Inbox 2.0 (`CONVERSATION_RESOLVED`) or an agent has set a current conversation to Pending (`CONVERSATION_SET_TO_PENDING`). This allows you to save conversation details to your existing CRM system. Other use cases include triggering CSAT surveys, case creation, and expanded customer insights from social media profiles.\n\n### Configure write back URL\n\nTo push data from Inbox 2.0 into your CRM, you need to implement a POST endpoint that accepts a JSON request and returns a JSON response. The request contains write back payload, and the response's status code represents your ability to parse the payload and the CRM's ability to ingest the data.\n\nConfigure the endpoint as the `Write Back URL`:\n\n 1. Go to `Admin settings`.\n 2. Expand `Integration and APIs`.\n 3. Select `CRM`.\n\n### Handle the request\n\nWhen the endpoint in your system is ready and configured in Inbox 2.0, and relevant events occur within Inbox 2.0, requests are sent to the configured endpoint. All decoded write back payloads have the following fields:\n\n - `type`: A string that defines what kind of write back request is being processed. Used to suggest the structure of the `data` field.\n - `version`: A number designating the version of the `type` of request. Used to mark changes to the `data` field structure.\n - `idempotencyKey`: A string that uniquely identifies each event to write back. Used to help ensure that each request is processed only once.\n - `data`: An object that contains structured data for the specific `type` of write back. For example, a `CONVERSATION_RESOLVED` type event has fields such as `medium`, `channel`, `messages`\\*, `notes`, `topics`, `contactProfile`\\*, `attributes`\\*, and `agent`, among others.\n\n `*` Applies to all mediums except Twitter. Twitter handle, tweets, and direct message transcripts, and Twitter medium contact attributes (Twitter name, number of followers, etc.), are not sent in the payload, to conform to Twitter's data use policies.\n\n### CONVERSATION_RESOLVED and CONVERSATION_SET_TO_PENDING events\n\nEvents with a type of either `CONVERSATION_RESOLVED` or `CONVERSATION_SET_TO_PENDING` have the structure defined.\n\n### Write back retry\n\nIf the write back request times out or the endpoint returns a non-success (2XX) status code, Inbox 2.0 retries with exponential backoffs. The requests are sent again with a delay of 1, 2, 4, 8, 16, and 32 hours after each retry (a total of 6 requests), as long as the request does not succeed. If the request still has not succeeded after 6 requests, we store the failed request details for future reference. We recommend that you inspect the endpoint logs regularly to ensure that write back requests are properly processed the first time. To distinguish between unique events, each request is given an Idempotency key.\n\n### Idempotency key\n\nThe write back requests from Inbox 2.0 make use of an Idempotency key, which allows the write back endpoint to ensure it processes each request once. This key helps when requests are sent multiple times during the retry schedule, so the endpoint can handle requests in an idempotent way. The key is unique per request.\n" operationId: crmWriteBack security: - Oauth2ClientCredentials: [] - SharedSecret: [] tags: - crm_webhooks requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CrmWriteBackRequest' responses: 2xx: description: 'The response status code represents your ability to parse the payload and the CRM''s ability to ingest the data. ' 4xx: description: 'The response status code represents your ability to parse the payload and the CRM''s ability to ingest the data. Any non-2XX response status code will be retried. ' 5xx: description: 'The response status code represents your ability to parse the payload and the CRM''s ability to ingest the data. Any non-2XX response status code will be retried. ' components: schemas: CrmWriteBackRequest: type: object description: The request contains write back payload. properties: timestamp: type: string format: date-time example: '2022-03-28T09:43:26.635984618Z' idempotencyKey: type: string description: A string that uniquely identifies each event. Used to help ensure that each request is processed only once. example: 6ebc6a78-d9e9-48be-b172-51eda40b7af8 version: type: integer description: 'A number designating the version of the `type` of request. Used to mark changes to the `data` field structure. ' example: 1 type: type: string enum: - CONVERSATION_RESOLVED - CONVERSATION_SET_TO_PENDING example: CONVERSATION_RESOLVED data: type: object properties: conversation: type: object properties: id: type: string example: 0ad42eef-a806-11eb-9642-f1ceb2f21def createdAt: type: string format: date-time example: '2022-03-28T09:42:27.004817183Z' previousStatus: type: string enum: - new - pending - resolved example: resolved currentStatus: type: string enum: - new - pending - resolved example: resolved medium: type: object properties: id: type: string example: fb channel: type: object properties: id: type: string example: 0-02534c5ac04-000-261ad914 name: type: string example: Inbox 2.0 support channel agent: type: object properties: id: type: string example: '11599' firstName: type: string example: John lastName: type: string example: Smith email: type: string example: john.smith@hootsuite.com contactProfile: type: object properties: id: type: string example: 693ed54b-a426-11eb-9363-cffd945b4b2f mediumContactProfileId: type: string example: bb7b0f8f00cc989b97f0725b primaryIdentifier: type: string example: John Smith secondaryIdentifier: type: string example: '+32439487192' pictureUrl: type: string example: https://example.com/my-picture.png statusUpdatedReason: type: string example: 5fd023f6-2e66-11eb-be07-092841b0717d - Response Not Required statusUpdatedComment: type: string example: not a question messages: type: array items: type: object properties: id: type: string direction: type: string enum: - INBOUND - OUTBOUND text: type: string example: - id: 0ad20c0d-a806-11eb-9642-f799d0e531de direction: INBOUND text: I have a question - id: 10664516-a806-11eb-9642-fbd9d431bd78 direction: OUTBOUND text: How can I help you? contactAttributes: type: array items: type: object properties: attribute: type: string example: null value: type: string source: type: string enum: - AGENT - MEDIUM - CRM_CONFIRMED example: - attribute: company value: My company source: AGENT - attribute: fb-profile-name value: John Smith source: MEDIUM - attribute: fb-profile-image value: https://image.com/sticky/default_profile_images/default_profile_normal.png source: MEDIUM - attribute: email value: john.smith@hootsuite.com source: CRM_CONFIRMED - attribute: first_name value: John source: CRM_CONFIRMED - attribute: last_name value: Smith source: CRM_CONFIRMED - attribute: company value: Hootsuite source: CRM_CONFIRMED notes: type: array items: type: object properties: id: type: string text: type: string creationUser: type: string creationTimestamp: type: string format: date-time example: - id: 8080a48b-3c70-11e1-8931-1e8999d73ad2 text: Here's a note on the conversation! creationUser: 0-019bd608cfc-001-0c6a2f5b creationTimestamp: '2018-06-28T00:17:09+00:00' topics: type: array items: type: object properties: id: type: string name: type: string example: - id: aa33389d-8a6a-11e8-b0d0-61f12b43ea29 name: Redeem Rewards - id: ee089cc2-8a6a-11e8-b0d0-942d249592dc name: Account Rewards CrmErrorNotificationsRequest: type: object properties: timestamp: type: string format: date-time version: type: integer example: 2 eventType: type: string enum: - DATA_IMPORT_ERROR messages: type: array description: an array of explicit error messages items: type: string example: - testMessage1 key:testKey1 value:testValue1 - testMessage2 key:testKey2 value:testValue2 CrmLookupAttributesResponse: type: object properties: autoConfirm: type: boolean description: 'The `autoConfirm` field tells Inbox 2.0 to automatically save the attributes without having to use the `Confirm` button. ' contactAttributes: type: array description: Array of JSON objects containing the lookup attributes items: additionalProperties: type: object properties: attribute: type: string value: type: string example: autoConfirm: true contactAttributes: - attribute: email value: fj@example.com - attribute: id value: '98765432' - attribute: first_name value: Fred - attribute: last_name value: Jones - attribute: last_order_number value: null securitySchemes: bearer-token: type: http scheme: bearer basic-auth: type: http scheme: basic Oauth2ClientCredentials: type: oauth2 flows: clientCredentials: tokenUrl: TO_BE_CONFIGURED_IN_INBOX_2_0 scopes: some_scope: TO_BE_CONFIGURED_IN_INBOX_2_0 SharedSecret: type: apiKey in: header name: X-Hootsuite-Signature x-provenance: generated: '2026-08-13' method: searched source: https://apidocs.hootsuite.com/docs/api/inbox/openapi/openapi.yaml note: Verbatim first-party OpenAPI 3.1 for the Hootsuite Inbox 2.0 API (formerly Sparkcentral), linked as service-desc for anchor https://platform.hootsuite.com/inbox/v1/ in Hootsuite's RFC 9727 API catalog at https://www.hootsuite.com/.well-known/api-catalog. ownership: servers[] https://platform.hootsuite.com, contact dev.support@hootsuite.com, license "Hootsuite Developer Terms and API License Agreement" - Hootsuite's own contract. x-tagGroups: - name: General tags: - rest-api-authentication - name: CRM API tags: - crm_introduction - crm_webhooks - crm_rest_api - name: Virtual Agent API tags: - vai_introduction - vai_webhooks - vai_rest_api - name: Real-time metrics API tags: - real_time_metrics_introduction - real_time_metrics_rest_api - name: User Presence API tags: - user_presence_introduction - user_presence_rest_api - name: Queue API tags: - queue_introduction - queue_rest_api - name: Proactive messaging API tags: - proactive_messaging_introduction - proactive_messaging_rest_api - name: Messenger SDK tags: - messenger_introduction - messenger_web_sdk