---
updatedAt: 2026-03-06T12:20:29.000Z
---

Fetch the complete documentation index at: https://developer.productboard.com/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Create webhook subscription

Creates a new webhook subscription to be actively notified on each change in the specified entities.

As part of subscription creation, Productboard validates the notification URL:
it must use `https`, have a publicly resolvable host, and not point to localhost,
loopback, site-local, or internal addresses.

**Scope requirements**: the OAuth2 application must have the scopes required by all requested event types.


# OpenAPI definition

```json
{
  "openapi": "3.1.1",
  "info": {
    "title": "Webhooks",
    "description": "Manages webhook subscriptions. Subscribe to entity change events and receive\nreal-time notifications at your endpoint.\n",
    "version": "2.0.0",
    "contact": {
      "name": "Productboard Support",
      "url": "https://support.productboard.com",
      "email": "support@productboard.com"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://www.productboard.com/terms"
    }
  },
  "servers": [
    {
      "url": "https://api.productboard.com/v2",
      "description": "Production server"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    },
    {
      "OAuth2": [
        "entities:read",
        "write:entities",
        "entities:delete"
      ]
    }
  ],
  "tags": [
    {
      "name": "webhooks",
      "description": "Operations for managing webhook subscriptions."
    }
  ],
  "paths": {
    "/webhooks": {
      "post": {
        "summary": "Create webhook subscription",
        "description": "Creates a new webhook subscription to be actively notified on each change in the specified entities.\n\nAs part of subscription creation, Productboard validates the notification URL:\nit must use `https`, have a publicly resolvable host, and not point to localhost,\nloopback, site-local, or internal addresses.\n\n**Scope requirements**: the OAuth2 application must have the scopes required by all requested event types.\n",
        "operationId": "createWebhook",
        "security": [
          {
            "BearerAuth": [
              "webhooks:write"
            ]
          }
        ],
        "tags": [
          "webhooks"
        ],
        "requestBody": {
          "description": "Definition of webhook subscription.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreateRequestBody"
              },
              "examples": {
                "default": {
                  "summary": "Subscribe to webhook events",
                  "value": {
                    "data": {
                      "fields": {
                        "name": "Feature changes webhook",
                        "events": [
                          {
                            "eventType": "feature.updated"
                          },
                          {
                            "eventType": "feature.created"
                          }
                        ],
                        "notification": {
                          "url": "https://example.destination.com/webhooks-handler",
                          "version": 1,
                          "headers": {
                            "authorization": "Bearer mySecret"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook subscription created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookReferenceResponse"
                },
                "examples": {
                  "created": {
                    "summary": "Subscription created",
                    "value": {
                      "data": {
                        "id": "550e8400-e29b-41d4-a716-446655440000",
                        "type": "webhook",
                        "links": {
                          "self": "https://api.productboard.com/v2/webhooks/550e8400-e29b-41d4-a716-446655440000"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequestResponse"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedResponse"
          },
          "403": {
            "$ref": "#/components/responses/ForbiddenResponse"
          },
          "408": {
            "$ref": "#/components/responses/RequestTimeoutResponse"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntityResponse"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequestsResponse"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerErrorResponse"
          }
        },
        "callbacks": {
          "entityChange": {
            "{$request.body#/data/fields/notification/url}": {
              "post": {
                "summary": "Webhook notification",
                "tags": [
                  "webhooks"
                ],
                "security": [
                  {
                    "CallbackAuthorization": []
                  }
                ],
                "description": "Sent whenever a subscribed entity change event occurs.\n\n- Timeout: **5 seconds** (end-to-end)\n- Return any `2XX` status to acknowledge receipt\n- Prefer `204 No Content` — response body is ignored\n- Failed deliveries are retried with exponential backoff (up to 8 attempts: 1m, 3m, 9m, ...)\n- Process notifications asynchronously and return immediately to avoid timeouts\n- Too many timeouts or failures may result in Productboard **disabling the subscription** with notification loss\n\n**Security**: configure `data.fields.notification.headers.authorization` to authenticate\nincoming requests from Productboard.\n",
                "operationId": "webhookNotification",
                "requestBody": {
                  "description": "Webhook notification payload.",
                  "required": true,
                  "content": {
                    "application/json": {
                      "schema": {
                        "$ref": "#/components/schemas/WebhookNotificationRequestBody"
                      }
                    }
                  }
                },
                "responses": {
                  "200": {
                    "description": "Any `2XX` status is treated as a successful delivery. `204 No Content` is preferred.\n"
                  },
                  "400": {
                    "$ref": "#/components/responses/BadRequestResponse"
                  },
                  "401": {
                    "$ref": "#/components/responses/UnauthorizedResponse"
                  },
                  "403": {
                    "$ref": "#/components/responses/ForbiddenResponse"
                  },
                  "408": {
                    "$ref": "#/components/responses/RequestTimeoutResponse"
                  },
                  "422": {
                    "$ref": "#/components/responses/UnprocessableEntityResponse"
                  },
                  "429": {
                    "$ref": "#/components/responses/TooManyRequestsResponse"
                  },
                  "500": {
                    "$ref": "#/components/responses/InternalServerErrorResponse"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "WebhookType": {
        "type": "string",
        "description": "Resource type identifier for webhook subscriptions.",
        "enum": [
          "webhook"
        ],
        "example": "webhook"
      },
      "WebhookReference": {
        "type": "object",
        "description": "Lightweight reference returned after creating a webhook subscription.",
        "required": [
          "id",
          "type",
          "links"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true,
            "description": "Unique identifier of the created webhook subscription.",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "type": {
            "$ref": "#/components/schemas/WebhookType"
          },
          "links": {
            "type": "object",
            "required": [
              "self"
            ],
            "properties": {
              "self": {
                "type": "string",
                "format": "uri",
                "description": "URL to fetch the full webhook subscription.",
                "example": "https://api.productboard.com/v2/webhooks/550e8400-e29b-41d4-a716-446655440000"
              }
            }
          }
        }
      },
      "WebhookReferenceResponse": {
        "type": "object",
        "description": "Response envelope for a newly created webhook subscription.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/WebhookReference"
          }
        }
      },
      "WebhookSubscriptionFields": {
        "type": "object",
        "description": "Domain attributes for creating a webhook subscription.",
        "required": [
          "name",
          "events",
          "notification"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Human-readable label for this subscription.",
            "minLength": 1,
            "maxLength": 255,
            "example": "Feature changes webhook"
          },
          "events": {
            "type": "array",
            "description": "Event types to subscribe to. At least one required; no duplicates allowed.\nThe full list of supported values is defined in the `WebhookEventType` schema.\n",
            "minItems": 1,
            "uniqueItems": true,
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          },
          "notification": {
            "$ref": "#/components/schemas/WebhookNotificationConfig"
          }
        }
      },
      "WebhookCreateRequestBody": {
        "type": "object",
        "description": "Request body for creating a webhook subscription.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "fields"
            ],
            "properties": {
              "fields": {
                "$ref": "#/components/schemas/WebhookSubscriptionFields"
              }
            }
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "required": [
          "eventType"
        ],
        "properties": {
          "eventType": {
            "$ref": "#/components/schemas/WebhookEventType"
          }
        }
      },
      "WebhookEventType": {
        "type": "string",
        "description": "Identifies the type of entity change that triggers a webhook notification.",
        "enum": [
          "feature.created",
          "feature.updated",
          "feature.deleted",
          "component.created",
          "component.updated",
          "product.created",
          "product.updated",
          "release.created",
          "release.updated",
          "release.deleted",
          "feature-release-assignment.updated",
          "hierarchy-entity.custom-field-value.updated",
          "note.created",
          "note.updated",
          "note.deleted",
          "insight.created",
          "insight.deleted",
          "key-result.created",
          "key-result.updated",
          "key-result.deleted",
          "objective.created",
          "objective.updated",
          "objective.deleted",
          "initiative.created",
          "initiative.updated",
          "initiative.deleted"
        ]
      },
      "WebhookNotificationConfig": {
        "type": "object",
        "description": "Where and how Productboard sends notifications (used in create requests).",
        "required": [
          "url",
          "version"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "pattern": "https://.+",
            "description": "HTTPS endpoint to receive notifications. Requirements:\n- Must use `https` (encrypted)\n- Must be publicly reachable (no `localhost`, private IPs, or internal Kubernetes addresses)\n",
            "example": "https://example.destination.com/webhooks-handler",
            "maxLength": 1024
          },
          "version": {
            "$ref": "#/components/schemas/WebhookPayloadVersion"
          },
          "headers": {
            "$ref": "#/components/schemas/WebhookNotificationHeaders"
          }
        }
      },
      "WebhookPayloadVersion": {
        "type": "integer",
        "description": "Version of the notification payload structure. Currently only `1` is supported.",
        "enum": [
          1
        ],
        "example": 1
      },
      "WebhookNotificationHeaders": {
        "type": "object",
        "description": "Custom headers included in every outgoing request from Productboard to your endpoint\n(notifications). Use this to authenticate incoming requests.\n",
        "properties": {
          "authorization": {
            "type": "string",
            "description": "Raw value sent in the `Authorization` header. Supports any scheme:\n- `Basic aGVsbG86d29ybGQ=`\n- `Bearer <token>`\n- Any custom value\n",
            "example": "Bearer myWebhookSecret",
            "maxLength": 2048
          }
        }
      },
      "WebhookNotificationPayloadV1": {
        "type": "object",
        "description": "Notification payload delivered to the subscriber endpoint.",
        "required": [
          "eventType",
          "links"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Entity ID that changed. Absent for `hierarchy-entity.custom-field-value.updated`\nand `feature-release-assignment.updated`.\n",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "eventType": {
            "$ref": "#/components/schemas/WebhookEventType"
          },
          "links": {
            "description": "Links related to this event.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/LinksTarget"
              },
              {
                "$ref": "#/components/schemas/LinksFeatureNote"
              }
            ]
          },
          "updatedAttributes": {
            "type": "array",
            "description": "Names of the API fields that changed. Only present on `*.updated` events.\nUses public API field names (e.g., `status`, `name`, `parent`).\n",
            "items": {
              "type": "string",
              "example": "status"
            }
          }
        }
      },
      "WebhookNotificationRequestBody": {
        "type": "object",
        "description": "Request body delivered to the subscriber endpoint on each entity change event.",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "$ref": "#/components/schemas/WebhookNotificationPayloadV1"
          }
        }
      },
      "LinksTarget": {
        "type": "object",
        "description": "Standard V1 link — points to the changed entity.",
        "required": [
          "target"
        ],
        "properties": {
          "target": {
            "type": "string",
            "format": "uri",
            "description": "API URL of the entity whose change triggered this notification.",
            "example": "https://api.productboard.com/features/550e8400-e29b-41d4-a716-446655440000"
          }
        }
      },
      "LinksFeatureNote": {
        "type": "object",
        "description": "V1 link shape for insight events — references both feature and note.",
        "required": [
          "feature",
          "note"
        ],
        "properties": {
          "feature": {
            "type": "string",
            "format": "uri",
            "example": "https://api.productboard.com/features/550e8400-e29b-41d4-a716-446655440000"
          },
          "note": {
            "type": "string",
            "format": "uri",
            "example": "https://api.productboard.com/notes/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          }
        }
      },
      "ErrorSource": {
        "type": "object",
        "description": "An object containing references to the source of the error. It helps with location of the problematic field or parameter.\n",
        "properties": {
          "pointer": {
            "type": "string",
            "description": "JSON Pointer [RFC6901] to the associated entity in the request document.\n",
            "example": "/data/fields/status"
          },
          "parameter": {
            "type": "string",
            "description": "Query parameter causing the error.\nUsed when the error is related to a query parameter rather than request body.\n",
            "example": "pageCursor"
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Individual error object containing the details and context.\n",
        "required": [
          "code",
          "title",
          "detail"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "A unique, machine-readable and stable code that identifies this error.\nMust be consistent across all services and provide clear categorization.\nFormat: `category.subcategory.specificError`\n",
            "examples": [
              "auth.accessDenied",
              "resource.notFound"
            ]
          },
          "title": {
            "type": "string",
            "description": "A short, human-readable summary of the problem that doesn't change\nfrom occurrence to occurrence of the problem. Should provide immediate\nunderstanding of the error type.\n",
            "maxLength": 255,
            "example": "Missing required field"
          },
          "detail": {
            "type": "string",
            "description": "A human-readable explanation specific to this occurrence of the problem.\nMay include suggestions for resolution but should not expose sensitive information.\n",
            "maxLength": 1000,
            "example": "Field 'status' is required."
          },
          "source": {
            "$ref": "#/components/schemas/ErrorSource"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "description": "Standard error response structure.\n",
        "required": [
          "id",
          "errors"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "A unique identifier for this specific occurrence of the problem.\nIt carries unique request/response ID which allows client\nand server to reference the exact instance of the error\nin logs or support communication.\n"
          },
          "errors": {
            "type": "array",
            "description": "Array of error objects, multiple errors can be returned.",
            "items": {
              "$ref": "#/components/schemas/Error"
            },
            "minItems": 1
          }
        }
      }
    },
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "API token authentication. Use your API token as the value of the Authorization header"
      },
      "OAuth2": {
        "type": "oauth2",
        "description": "OAuth 2.0 authentication. Use this for user-authorized access to Productboard API.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://app.productboard.com/oauth/authorize",
            "tokenUrl": "https://app.productboard.com/oauth/token",
            "scopes": {
              "entities:read": "Read access to entities (features, components, initiatives, etc.)",
              "write:entities": "Write access to create and modify entities",
              "entities:delete": "Delete entities"
            }
          }
        }
      }
    },
    "responses": {
      "BadRequestResponse": {
        "description": "Bad Request - Invalid input format or malformed request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "schemaInvalid": {
                "summary": "Request does not satisfy schema",
                "value": {
                  "id": "req-1234567890",
                  "errors": [
                    {
                      "code": "request.invalid",
                      "title": "Request schema invalid",
                      "detail": "The request does not satisfy the required schema. Field 'type' is required but was not provided.",
                      "source": {
                        "pointer": "/data/type"
                      }
                    }
                  ]
                }
              },
              "fieldInvalid": {
                "summary": "Invalid field value type",
                "value": {
                  "id": "req-1234567891",
                  "errors": [
                    {
                      "code": "request.invalid",
                      "title": "Request schema invalid",
                      "detail": "Field 'fields/name' must be a string with maximum length of 255 characters, but received a number.",
                      "source": {
                        "pointer": "/data/fields/name"
                      }
                    }
                  ]
                }
              },
              "malformedJson": {
                "summary": "Malformed JSON syntax",
                "value": {
                  "id": "req-1234567892",
                  "errors": [
                    {
                      "code": "request.malformed",
                      "title": "Request malformed",
                      "detail": "The request body contains invalid JSON syntax. Expected ',' or '}' at line 5, column 12."
                    }
                  ]
                }
              },
              "invalidContentType": {
                "summary": "Invalid content type",
                "value": {
                  "id": "req-1234567893",
                  "errors": [
                    {
                      "code": "request.malformed",
                      "title": "Request malformed",
                      "detail": "The Content-Type header must be 'application/json'. Received 'text/plain'."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "UnauthorizedResponse": {
        "description": "Unauthorized - Missing or invalid authentication credentials",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "missingCredentials": {
                "summary": "Missing authentication credentials",
                "value": {
                  "id": "req-2234567890",
                  "errors": [
                    {
                      "code": "auth.invalid",
                      "title": "Invalid authentication",
                      "detail": "Authentication credentials are missing."
                    }
                  ]
                }
              },
              "invalidToken": {
                "summary": "Invalid or expired token",
                "value": {
                  "id": "req-2234567891",
                  "errors": [
                    {
                      "code": "auth.invalid",
                      "title": "Invalid authentication",
                      "detail": "The provided authentication credentials are invalid or have expired. Please obtain new credentials and try again."
                    }
                  ]
                }
              },
              "revokedCredentials": {
                "summary": "Revoked credentials",
                "value": {
                  "id": "req-2234567892",
                  "errors": [
                    {
                      "code": "auth.invalid",
                      "title": "Invalid authentication",
                      "detail": "The authentication credentials have been revoked. Please contact your workspace administrator."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "ForbiddenResponse": {
        "description": "Forbidden - Insufficient permissions",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "permissionDenied": {
                "summary": "Permission denied for resource",
                "value": {
                  "id": "req-3234567890",
                  "errors": [
                    {
                      "code": "auth.accessDenied",
                      "title": "Access denied",
                      "detail": "You do not have permission to perform this operation on the specified resource. Contact your workspace administrator to request access."
                    }
                  ]
                }
              },
              "insufficientScope": {
                "summary": "Insufficient OAuth scopes",
                "value": {
                  "id": "req-3234567891",
                  "errors": [
                    {
                      "code": "auth.accessDenied",
                      "title": "Access denied",
                      "detail": "Your authentication token lacks the required OAuth scopes to perform this operation. Please re-authenticate with the necessary scopes."
                    }
                  ]
                }
              },
              "readOnlyRole": {
                "summary": "Read-only role restriction",
                "value": {
                  "id": "req-3234567892",
                  "errors": [
                    {
                      "code": "auth.accessDenied",
                      "title": "Access denied",
                      "detail": "Your user role does not permit this operation. Please contact your workspace administrator."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "RequestTimeoutResponse": {
        "description": "Request Timeout - The server did not receive a complete request within the allowed time",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "requestTimeout": {
                "summary": "Request timeout",
                "value": {
                  "id": "req-5234567890",
                  "errors": [
                    {
                      "code": "request.timeout",
                      "title": "Request timeout",
                      "detail": "The request took too long to process and was terminated. Try simplifying your request or try again later."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "TooManyRequestsResponse": {
        "description": "Too Many Requests - API rate limit exceeded, reduce request frequency and retry after the indicated time",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "rateLimitExceeded": {
                "summary": "Rate limit exceeded",
                "value": {
                  "id": "req-7234567890",
                  "errors": [
                    {
                      "code": "rate.limitExceeded",
                      "title": "Rate limit exceeded",
                      "detail": "You have exceeded the allowed number of API requests. Please wait before making additional requests."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "InternalServerErrorResponse": {
        "description": "Internal Server Error - An unexpected error occurred on the server, please retry or contact support",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "internalServerError": {
                "summary": "Unexpected server error",
                "value": {
                  "id": "req-8234567890",
                  "errors": [
                    {
                      "code": "internal.serverError",
                      "title": "Internal server error",
                      "detail": "An unexpected error occurred while processing your request. Please try again later. If the problem persists, contact support with the correlation ID."
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "UnprocessableEntityResponse": {
        "description": "Unprocessable Entity - Validation failed (e.g., missing required fields, unknown fields)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "validationFailed": {
                "summary": "Validation failed",
                "value": {
                  "id": "req-6234567890",
                  "errors": [
                    {
                      "code": "validation.failed",
                      "title": "Validation failed",
                      "detail": "One or more fields failed validation.",
                      "source": {
                        "pointer": "/data/type"
                      }
                    }
                  ]
                }
              },
              "validationMultipleFields": {
                "summary": "Multiple validation errors",
                "value": {
                  "id": "req-6234567891",
                  "errors": [
                    {
                      "code": "validation.failed",
                      "title": "Validation failed",
                      "detail": "The field 'name' cannot be empty.",
                      "source": {
                        "pointer": "/data/fields/name"
                      }
                    },
                    {
                      "code": "validation.failed",
                      "title": "Validation failed",
                      "detail": "The field 'type' has an unsupported value.",
                      "source": {
                        "pointer": "/data/fields/type"
                      }
                    }
                  ]
                }
              },
              "referenceNotFound": {
                "summary": "Referenced resource not found",
                "value": {
                  "id": "req-6234567892",
                  "errors": [
                    {
                      "code": "validation.referenceNotFound",
                      "title": "Referenced resource not found",
                      "detail": "The referenced resource could not be found or you do not have access to it.",
                      "source": {
                        "pointer": "/data/fields/id"
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "x-readme": {
    "samples-languages": [
      "shell",
      "javascript",
      "ruby"
    ]
  }
}
```