Skip to main content
The webhooks journal API enables third-party integrators to subscribe to real-time events and retrieve historical event data. The subscription management API allows you to create and manage the subscriptions that drive which events appear in the journal.
Please note: these APIs are managed by API only because they are install-specific, and don’t work with the feature component functionality described in this article.

Authentication and scopes

All API endpoints require OAuth 2.0 authentication. The webhooks journal API uses a client credentials token to authorize actions on behalf of your app. Instructions for setting up this token can be found in the authentication overview. Based on the webhook management and journal endpoints you plan on using, include the corresponding scopes when generating your client credentials token:
  • developer.webhooks_journal.read: access journal data.
  • developer.webhooks_journal.subscriptions.read: retrieve subscription information.
  • developer.webhooks_journal.subscriptions.write: create, update, and delete subscriptions.
  • developer.webhooks_journal.snapshots.read: retrieve snapshot information.
  • developer.webhooks_journal.snapshots.write: create and manage object snapshots.
In addition to the management scopes above, you’ll also need to authorize any scopes that correspond to the object types you’re planning on subscribing to. For example, if you plan on subscribing to contact change events, you’ll need to authorize the crm.objects.contacts.read scope. For example, to generate a client credentials token with all available webhook journal and management permissions, you’d make the following API call:
curl --location 'https://api.hubapi.com/oauth/v1/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'client_id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \
  --data-urlencode 'client_secret=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \
  --data-urlencode 'scope=developer.webhooks_journal.read developer.webhooks_journal.subscriptions.read developer.webhooks_journal.subscriptions.write developer.webhooks_journal.snapshots.read developer.webhooks_journal.snapshots.write'
A successful request to the https://api.hubapi.com/oauth/v1/token endpoint will then return an access token you can include in the Authorization header of your requests:
Authorization: Bearer YOUR_ACCESS_TOKEN
You’ll use this access token to make requests to both the journal API and management API endpoints.

How to use the management and journal APIs

The management, journal, and snapshot APIs are designed to be used together:
  • Use the management API endpoints to subscribe to specific events, or filter event data.
  • After you’ve subscribed to specific events, you can use the journal API endpoints to access historical event data in chronological order. This allows you to query for subscription-related changes for the past three days. These endpoints are optimized for a performant response when checking object changes, especially compared to the alternative of using the CRM search API.
  • Use the snapshots API endpoints to create snapshots of CRM objects at specific points in time.
Get started by checking out the individual API guides:

Event data format

After you’ve created your webhook subscriptions, you can retrieve journal entries using the journal API endpoints. For example, when making a GET request to /webhooks-journal/journal/2026-03/earliest, the response will resemble the following:
{
  "url": "https://s3.amazonaws.com/bucket/path/to/journal/file.json",
  "expiresAt": "2024-01-01T12:00:00Z",
  "currentOffset": "550e8400-e29b-41d4-a716-446655440000"
}
When you retrieve the journal file using the url property in the response from these journal retrieval endpoints, the associated file contain JSON events in the following format:

Contact creation event example

{
  "offset": "0197f5c0-4d9b-7932-83ec-06d56430c359",
  "journalEvents": [
    {
      "type": "crmObject",
      "portalId": 123456,
      "occurredAt": "2025-07-10T19:11:49.524Z",
      "action": "CREATE",
      "objectTypeId": "0-1",
      "objectId": 125119015226
    }
  ],
  "publishedAt": "2025-07-10T19:11:50.172Z"
}

Contact update event example

{
  "offset": "0198197a-f0ac-7906-8bfe-a569398704ac",
  "journalEvents": [
    {
      "type": "crmObject",
      "portalId": 145534986,
      "occurredAt": "2025-07-17T17:42:23.540Z",
      "action": "UPDATE",
      "objectTypeId": "0-1",
      "objectId": 11073740016,
      "propertyChanges": {
        "lastname": "smith"
      }
    }
  ],
  "publishedAt": "2025-07-17T17:42:24.173Z"
}

Association added example

{
  "offset": "0198199f-f454-7ee4-9766-779433c4864d",
  "journalEvents": [
    {
      "type": "association",
      "portalId": 145534986,
      "occurredAt": "2025-07-17T18:22:49.134Z",
      "action": "ASSOCIATION_ADDED",
      "fromObjectId": 11073740013,
      "toObjectId": 11073740016,
      "fromObjectTypeId": "2-134027637",
      "toObjectTypeId": "2-134027637",
      "associationTypeId": 17,
      "associationCategory": "USER_DEFINED",
      "isPrimary": false
    }
  ]
}

App install and uninstall event journal payloads

If you subscribe to app install and uninstall events, the corresponding journal events will resemble the following:
{
  "offset": "01983883-ee94-71df-8c20-9f3cbf039015",
  "journalEvents": [
    {
      "type": "app_lifecycle_event",
      "occurredAt": "2025-07-23T18:20:23.719Z",
      "action": "APP_INSTALL",
      "portalId": 123456,
      "eventTypeId": "4-1909196",
      "properties": {
        "hs_app_id": 1234567,
        "hs_app_install_level": "PORTAL",
        "hs_initiating_user_id": 987654321,
        "hs_user_id": 12345
      }
    }
  ],
  "publishedAt": "2025-07-23T18:20:27.196Z"
}
Note that the following event type IDs (eventTypeId) are available based on the triggered event:
  • 4-1909196: App install event
  • 4-1916193: App uninstall event

Error handling

The API uses standard HTTP status codes to indicate success or failure:

Success codes

  • 200 OK: request successful
  • 204 No Content: request successful with no response body

Error codes

  • 400 Bad Request: invalid request parameters
  • 401 Unauthorized: missing or invalid authentication token
  • 403 Forbidden: insufficient permissions for the requested operation
  • 404 Not Found: requested resource not found
  • 429 Too Many Requests: rate limit exceeded
  • 500 Internal Server Error: server error

Error response format

Error responses include details in the following format:
{
  "status": "error",
  "message": "Invalid subscription type provided",
  "correlationId": "550e8400-e29b-41d4-a716-446655440000",
  "category": "VALIDATION_ERROR"
}

Rate limits

The following rate limits apply to the webhooks journal and subscription management APIs:
  • Journal API: 100 requests per second per app
  • Subscriptions API: 50 requests per second per app
  • Snapshots API: 10 requests per second per app
When you exceed rate limits, you’ll receive a 429 Too Many Requests response with a Retry-After header indicating when you can retry.
Please note: the limits above are subject to change. These rate limits are distinct from the rest of HubSpot’s rate limits outlined here.

Best practices

Keep the following best practices in mind when using the webhooks journal API:

Journal processing

  • Sequential processing: process journal files in chronological order using the offset system.
  • Offset management: always store and use the currentOffset from responses for pagination.
  • URL expiration: journal URLs expire after a set time — download files promptly.
  • Error handling: implement exponential backoff for transient errors.

Subscription management

  • Specific filters: use object type and action filters to reduce unnecessary events.
  • Property selection: only subscribe to properties you need to minimize data transfer.
  • Regular cleanup: remove unused subscriptions to maintain performance.

Snapshot usage

  • Batch requests: group multiple snapshot requests into single API calls.
  • Property optimization: only request properties you need for each object.
  • Timing considerations: snapshots reflect object state at request time.
Last modified on April 14, 2026