Last modified: November 18, 2025
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. 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.
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
Required scopes
You’ll need to authorize the following scopes based on the webhook management and journal endpoints you’re planning on using:
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.contacts.read scope.
A full list of available scopes in this reference article.
Webhooks journal API
The Journal API provides access to historical event data stored in chronological order. This API allows you to query for changes that occurred for your subscriptions for the past 3 days. These endpoints are optimized to provide a performant response to check the corresponding data that changed for an object or property that you’re subscribed to, especially compared to the alternative of using the CRM search API.
Get earliest journal entry
To retrieve the earliest available journal entry for your app, ensure your app has the developer.webhooks_journal.read 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 developer.webhooks_journal.read 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 retrieve the next journal entry after a specific offset, authorize the developer.webhooks_journal.read 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 developer.webhooks_journal.subscriptions.write 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.
| Field | Type | Description |
|---|
objectTypeId * | String | The object type identifier (e.g., "0-1" for contacts). |
subscriptionType * | String | Type of subscription: "OBJECT" or "ASSOCIATION". |
portalId * | Number | The ID of the HubSpot account to subscribe to events for. |
actions * | Array | A 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.
|
properties | Array | Specific properties to include in events. |
objectIds | Array | Specific object IDs to subscribe to. |
associatedObjectTypeIds | Array | Associated 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"
}
Create an app install or uninstall event subscription
To subscribe to app install or app uninstall events, use your client credentials token to make a POST request to /webhooks/v4/subscriptions with the following request body:
{
"subscriptionType": "APP_LIFECYCLE_EVENT",
"eventTypeId": "4-1909196",
"properties": ["string"]
}
You can then retrieve journal events and any app install or uninstall events will be returned in the response.
Note that the following event type IDs (eventTypeId) are available based on the event you want to subscribe to:
4-1909196: App install event
4-1916193: App uninstall event
Get all subscriptions
To retrieve all subscriptions, authorize the developer.webhooks_journal.subscriptions.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 developer.webhooks_journal.subscriptions.write 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 developer.webhooks_journal.subscriptions.write 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 developer.webhooks_journal.snapshots.write 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.
| Field | Type | Description |
|---|
snapshotRequests | Array | A list of snapshot request objects. |
portalId | Number | The ID of the HubSpot account where the object exists. |
objectId | Number | The ID of the CRM object to snapshot. |
objectTypeId | String | Object type identifier (e.g., “0-1” for contacts). |
properties | Array | Array 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:
objectTypeId | Object type |
|---|
0-1 | Contacts |
0-2 | Companies |
0-3 | Deals |
0-4 | Engagements |
0-5 | Tickets |
0-7 | Products |
0-8 | Line Items |
0-11 | Conversations |
0-14 | Quotes |
0-15 | Forms |
0-19 | Feedback Submissions |
0-20 | Attributions |
0-27 | Tasks |
0-45 | Object Lists |
Please note: object type IDs follow the format {metaTypeId}-{innerId}. Standard HubSpot objects use metaTypeId 0, while custom objects use metaTypeId 2.
When you retrieve journal files from the URLs provided by the webhooks journal API, the files contain JSON events in the following format:
{
"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"
}
{
"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"
}
{
"offset": "0197f5c0-4d9b-7932-83ec-06d56430c359",
"journalEvents": [
{
"type": "crmObject",
"portalId": 123456,
"occurredAt": "2025-07-10T19:11:49.524Z",
"action": "UPDATE",
"objectTypeId": "0-1",
"objectId": 125119015226,
"propertyChanges": {
"lastname": "smith"
}
}
],
"publishedAt": "2025-07-10T19:11:50.172Z"
}
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.156Z"
}
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, possibly due to an backend issue from HubSpot
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 webhook journal and v4 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 during the current beta period. These rate limits are distinct from the rest of HubSpot’s rate limits outlined here.
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