This guide covers the webhooks journal API and the subscription management API. 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
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.objects.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, allowing you to query for changes that occurred for your subscriptions over the past 3 days. These endpoints are optimized for performant access to object and property change data, especially compared to alternatives like the CRM search API.
The journal API has two variants:
- Global journal (
/webhooks-journal/journal/2026-03/...): returns events across all accounts where the app is installed.
- Local journal (
/webhooks-journal/journal-local/2026-03/...): returns events scoped to a specific account where the app is installed, identified by the optional installPortalId query parameter.
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-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"
}
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, make a GET request to /webhooks-journal/journal/2026-03/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"
}
Get next journal entry
To retrieve the next journal entry after a specific offset, make a GET request to /webhooks-journal/journal/2026-03/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 make a GET request to /webhooks-journal/journal/2026-03/offset/550e8400-e29b-41d4-a716-446655440000/next.
{
"url": "https://s3.amazonaws.com/bucket/path/to/journal/file.json",
"expiresAt": "2024-01-01T12:00:00Z",
"currentOffset": "550e8400-e29b-41d4-a716-446655440000"
}
Get journal status
To retrieve the status of a journal snapshot operation, make a GET request to /webhooks-journal/journal/2026-03/status/{statusId}, where statusId is the UUID returned when a snapshot was requested.
Local journal
The local journal endpoints follow the same pattern as the global journal, but are scoped to a specific account. Use the installPortalId query parameter to specify the account.
GET /webhooks-journal/journal-local/2026-03/earliest
GET /webhooks-journal/journal-local/2026-03/latest
GET /webhooks-journal/journal-local/2026-03/offset/{offset}/next
GET /webhooks-journal/journal-local/2026-03/status/{statusId}
Subscriptions API
The subscriptions API allows you to manage event subscriptions for your app.
Create or update a subscription
To create a new subscription, authorize the developer.webhooks_journal.subscriptions.write scope for your app, then make a POST request to /webhooks-journal/subscriptions/2026-03. In the body of your request, include the properties for the subscription you want to create, detailed in the table below.
| 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 shows how to create 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.
{
"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-journal/subscriptions/2026-03 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-journal/subscriptions/2026-03.
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 a 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-journal/subscriptions/2026-03/{subscriptionId}.
For example, if the ID of the subscription you want to delete is 789, you’d make a DELETE request to https://api.hubapi.com/webhooks-journal/subscriptions/2026-03/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-journal/subscriptions/2026-03/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.
Subscription filters
Subscription filters allow you to attach additional filtering criteria to an existing subscription, further narrowing which events are written to the journal.
To create a filter, make a POST request to /webhooks-journal/subscriptions/2026-03/filters.
To retrieve a specific filter by its ID, make a GET request to /webhooks-journal/subscriptions/2026-03/filters/{filterId}.
To retrieve all filters for a specific subscription, make a GET request to /webhooks-journal/subscriptions/2026-03/filters/subscription/{subscriptionId}.
To delete a filter, make a DELETE request to /webhooks-journal/subscriptions/2026-03/filters/{filterId}. A successful request will return a 204 No Content response.
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-journal/snapshots/2026-03/crm. In the request body, include the snapshotRequests property with the objects you want to snapshot.
{
"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. |
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": "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 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.