Last modified: August 22, 2025

Using the webhooks journal and webhooks v4 APIs

This guide provides guidance for the HubSpot Webhooks Journal system and the latest version (v4) of the webhooks subscription API. The webhooks journal API enables third-party integrators to subscribe to real-time events, retrieve historical event data, and manage snapshots of CRM objects, and the webhooks v4 API builds on the previous version of the webhooks subscription API.
Please note: these APIs provide a new subscription model and are not currently compatible with the previous version (v3) of the webhooks API:
  • The webhooks journal and management functionality described in this guide are managed by API only because they are install-specific, and don’t work with the feature component functionality described in this article.
  • The webhooks v4 endpoints do not include a targetUrl field, as the subscriptions are written to the journal that’s polled by the app.

Authentication

All API endpoints require OAuth 2.0 authentication. Include your access token in the Authorization header of your requests:
Authorization: Bearer YOUR_ACCESS_TOKEN
Learn more about setting up OAuth for your app.

Required scopes

Different operations require different OAuth scopes:
  • webhooks.journal.data.access: access journal data and snapshots.
  • webhooks.journal.subscription.read: retrieve subscription information.
  • webhooks.journal.subscription.manage: create, update, and delete subscriptions.
  • webhooks.journal.snapshot.manage: create and manage object snapshots.

Webhooks journal API

The Journal API provides access to historical event data stored in chronological order.

Get earliest journal entry

To retrieve the earliest available journal entry for your app, ensure your app has the webhooks.journal.data.access scope, then make a GET request to /webhooks/v4/journal/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"
}
You can then retrieve the file at the specified url in the response for the associated journal data.

Get latest journal entry

To retrieve the latest available journal entry for your app, ensure your app has the webhooks.journal.data.access scope, then make a GET request to /webhooks/v4/journal/latest`. 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"
}
You can then retrieve the file at the specified url in the response for the associated journal data.

Get next journal entry

To retrieves the next journal entry after a specific offset, authorize the webhooks.journal.data.access scope for your app, then make a GET request to /webhooks/v4/journal/offset/{offset}/next. The offset path parameter is the UUID offset from the previous journal entry. For example, using the response from the previous sections, you’d then make a GET request to /webhooks/v4/journal/offset/550e8400-e29b-41d4-a716-446655440000/next. The code block below demonstrates an example response.
{
  "url": "https://s3.amazonaws.com/bucket/path/to/journal/file.json",
  "expiresAt": "2024-01-01T12:00:00Z",
  "currentOffset": "550e8400-e29b-41d4-a716-446655440000"
}
You can then retrieve the file at the specified url in the response for the associated journal data.

Subscriptions v4 API

The subscriptions API allows you to manage event subscriptions for your app.

Create or update subscriptions

To create a new subscription, authorize the webhooks.journal.subscriptions.manage scope for your app, then make a POST request to /webhooks/v4/subscriptions. In the body of your request, include the properties for the subscription you want to create, detailed in the table below.

Fields marked with * are required.

FieldTypeDescription
objectTypeId *StringThe object type identifier (e.g., "0-1" for contacts).
subscriptionType *StringType of subscription: "OBJECT" or "ASSOCIATION".
portalId *NumberThe ID of the HubSpot account to subscribe to events for.
actions *ArrayA list of actions to subscribe to. The following actions are available:
  • CREATE: Object creation events.
  • UPDATE: Object modification events.
  • DELETE: Object deletion events.
  • MERGE: Object merge events.
  • RESTORE: Object restoration events.
  • ASSOCIATION_ADDED: association creation events.
  • ASSOCIATION_REMOVED: association removal events.
  • SNAPSHOT: snapshot generation.
propertiesArraySpecific properties to include in events.
objectIdsArraySpecific object IDs to subscribe to.
associatedObjectTypeIdsArrayAssociated object types to include.
The following code example provides an example of creating a subscription with a subscriptionType of OBJECT to subscribe to creation and deletion events for a set of specific contacts, as well as any updates to the email, first name, or last name properties of those contacts. Note that the portalId property specifies the ID of the HubSpot account to subscribe to the corresponding subscription type.
{
  "objectTypeId": "0-1",
  "subscriptionType": "OBJECT",
  "portalId": 12345,
  "actions": ["CREATE", "UPDATE", "DELETE"],
  "properties": ["email", "firstname", "lastname"],
  "objectIds": [1001, 1002, 1003]
}
The resulting response would be:
{
  "id": 789,
  "appId": 456,
  "subscriptionType": "OBJECT",
  "objectTypeId": "0-1",
  "portalId": 12345,
  "actions": ["CREATE", "UPDATE", "DELETE"],
  "properties": ["email", "firstname", "lastname"],
  "objectIds": [1001, 1002, 1003],
  "createdBy": 999,
  "createdAt": "2024-01-01T10:00:00Z",
  "updatedAt": "2024-01-01T10:00:00Z",
  "deletedAt": null
}
The following code snippet demonstrates how to create a subscription with a subscriptionType of ASSOCIATION, which will trigger any time a set of specific contacts (provided via the objectIds field) are associated with a company, or if any existing associations are removed:
{
  "objectTypeId": "0-1",
  "subscriptionType": "ASSOCIATION",
  "portalId": 12345,
  "actions": ["ASSOCIATION_ADDED", "ASSOCIATION_REMOVED"],
  "objectIds": [1001, 1002, 1003],
  "associatedObjectTypeIds": ["0-2"]
}
The corresponding response would be:
{
    "id": 60001005,
    "appId": 936515,
    "subscriptionType": "ASSOCIATION",
    "objectTypeId": "0-1",
    "portalId": 12345,
    "actions": [
        "ASSOCIATION_REMOVED",
        "ASSOCIATION_ADDED"
    ],
    "objectIds": [
        1001,
        1002,
        1003
    ],
    "createdAt": "2025-07-10T18:30:38.062Z",
    "updatedAt": "2025-07-10T18:30:38.062Z"
}

Get all subscriptions

To retrieve all subscriptions, authorize the webhooks.journal.subscription.read scope for your app, then make a GET request to /webhooks/v4/subscriptions. The response will resemble the following:
{
  "results": [
    {
      "id": 789,
      "appId": 456,
      "subscriptionType": "OBJECT",
      "objectTypeId": "0-1",
      "portalId": 12345,
      "actions": ["CREATE", "UPDATE"],
      "properties": ["email", "firstname", "lastname"],
      "objectIds": [],
      "associatedObjectTypeIds": [],
      "createdBy": 999,
      "createdAt": "2024-01-01T10:00:00Z",
      "updatedAt": "2024-01-01T10:00:00Z",
      "deletedAt": null
    }
  ]
}

Delete subscription

To delete a specific subscription by its ID, authorize the webhooks.journal.subscription.manage scope for your app, then make a DELETE request to /webhooks/v4/subscriptions/{subscriptionId}. You can retrieve a list of subscriptions by following the steps in the sections above, then use the ID for one of the entries in the response to delete it using this endpoint. For example, if the ID of the subscriptions you want to delete is 789, you’d make a DELETE request to https://api.hubapi.com/webhooks/v4/subscriptions/789. A successful request will result in a response of 204 No Content.

Delete all subscriptions for an account

To delete all subscriptions for a specific HubSpot account, authorize the webhooks.journal.subscription.manage scope for your app, then make a DELETE request to /webhooks/v4/subscriptions/portals/{portalId}, using the ID of the HubSpot account as the portalId path parameter. A successful deletion request will return a response of 204 No Content.

Snapshots API

The Snapshots API allows you to trigger snapshots of CRM objects for specific points in time.

Create CRM object snapshots

To create snapshots for multiple CRM objects in a single batch request, ensure you’ve authorized the webhooks.journal.snapshot.manage scope, then make a POST request to /webhooks/v4/snapshots/crm. In the request body, include the snapshotRequests property, which should include the snapshots for the objects you want to update. For example, your request body might resemble the following if you wanted to trigger snapshots for two contacts with two different sets of properties.
{
  "snapshotRequests": [
    {
      "portalId": 12345,
      "objectId": 1001,
      "objectTypeId": "0-1",
      "properties": ["email", "firstname", "lastname", "phone"]
    },
    {
      "portalId": 12345,
      "objectId": 1002,
      "objectTypeId": "0-1",
      "properties": ["email", "company"]
    }
  ]
}
The available properties for this endpoint are listed in the table below. All fields are required.
FieldTypeDescription
snapshotRequestsArrayA list of snapshot request objects.
portalIdNumberThe ID of the HubSpot account where the object exists.
objectIdNumberThe ID of the CRM object to snapshot.
objectTypeIdStringObject type identifier (e.g., “0-1” for contacts).
propertiesArrayArray of property names to include in the snapshot.
A successful request will return a response of 204 No Content.

Common object type IDs

Here are the standard HubSpot object type IDs you’ll use in API requests:
objectTypeIdObject type
0-1Contacts
0-2Companies
0-3Deals
0-4Engagements
0-5Tickets
0-7Products
0-8Line Items
0-11Conversations
0-14Quotes
0-15Forms
0-19Feedback Submissions
0-20Attributions
0-27Tasks
0-45Object Lists
Please note: object type IDs follow the format {metaTypeId}-{innerId}. Standard HubSpot objects use metaTypeId 0, while custom objects use metaTypeId 2.

Event data format

When you retrieve journal files from the URLs provided by the Journal API, the files contain JSON events in the following format:

Contact creation event example

{
  "offset": "0197132b-2ed4-7ed1-8e9e-6d38b78930cf",
  "journalEvents": [
    {
      "type": "crmObject",
      "portalId": 22172136,
      "occurredAt": "2025-05-27T19:14:45.731Z",
      "action": "CREATE",
      "objectTypeId": "0-1",
      "objectId": 125119015226,
      "propertyChanges": {
        "hs_is_unworked": "true",
        "hs_pipeline": "contacts-lifecycle-pipeline",
        "hs_membership_has_accessed_private_content": "0"
      }
    }
  ],
  "publishedAt": "2025-05-27T19:14:46.613Z"
}

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": "2-134027637",
      "objectId": 11073740016,
      "propertyChanges": {
        "pet_name": "Scrappy 4"
      }
    }
  ],
  "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
    }
  ]
}

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, possibly due to an backend issue from HubSpot

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

API endpoints are subject to the following rate limits:
  • Journal API: 100 requests per minute per app
  • Subscriptions API: 50 requests per minute per app
  • Snapshots API: 10 requests per minute 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.

Best practices

Keep the following best practices 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
  • Batch Operations: Group subscription changes when possible
  • 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
  • Portal Awareness: Ensure objects exist in the specified portal before requesting snapshots
  • Timing Considerations: Snapshots reflect object state at request time

Error handling

  • Retry Logic: Implement exponential backoff for rate limits and temporary errors
  • Correlation IDs: Use correlation IDs from error responses when contacting support
  • Validation: Validate request data before sending to reduce error rates
  • Monitoring: Monitor API usage and error rates to detect issues early