From 34059c9fbde237d4cbd7ac83245812afbbf8996b Mon Sep 17 00:00:00 2001 From: mogery Date: Tue, 5 May 2026 21:18:50 +0200 Subject: [PATCH 1/4] monitoring docs --- api-reference/endpoint/monitor-check-get.mdx | 4 + .../endpoint/monitor-checks-list.mdx | 4 + api-reference/endpoint/monitor-create.mdx | 4 + api-reference/endpoint/monitor-delete.mdx | 4 + api-reference/endpoint/monitor-get.mdx | 4 + api-reference/endpoint/monitor-list.mdx | 4 + api-reference/endpoint/monitor-run.mdx | 4 + api-reference/endpoint/monitor-update.mdx | 4 + .../webhook-monitor-check-completed.mdx | 4 + api-reference/v2-openapi.json | 768 ++++++++++++++++++ api-reference/webhooks-openapi.json | 187 +++++ docs.json | 20 + features/monitoring.mdx | 277 +++++++ webhooks/events.mdx | 43 +- webhooks/overview.mdx | 3 +- 15 files changed, 1332 insertions(+), 2 deletions(-) create mode 100644 api-reference/endpoint/monitor-check-get.mdx create mode 100644 api-reference/endpoint/monitor-checks-list.mdx create mode 100644 api-reference/endpoint/monitor-create.mdx create mode 100644 api-reference/endpoint/monitor-delete.mdx create mode 100644 api-reference/endpoint/monitor-get.mdx create mode 100644 api-reference/endpoint/monitor-list.mdx create mode 100644 api-reference/endpoint/monitor-run.mdx create mode 100644 api-reference/endpoint/monitor-update.mdx create mode 100644 api-reference/endpoint/webhook-monitor-check-completed.mdx create mode 100644 features/monitoring.mdx diff --git a/api-reference/endpoint/monitor-check-get.mdx b/api-reference/endpoint/monitor-check-get.mdx new file mode 100644 index 00000000..37e4189c --- /dev/null +++ b/api-reference/endpoint/monitor-check-get.mdx @@ -0,0 +1,4 @@ +--- +title: "Get Monitor Check" +openapi: "/api-reference/v2-openapi.json GET /monitor/{monitorId}/checks/{checkId}" +--- diff --git a/api-reference/endpoint/monitor-checks-list.mdx b/api-reference/endpoint/monitor-checks-list.mdx new file mode 100644 index 00000000..9e43cff2 --- /dev/null +++ b/api-reference/endpoint/monitor-checks-list.mdx @@ -0,0 +1,4 @@ +--- +title: "List Monitor Checks" +openapi: "/api-reference/v2-openapi.json GET /monitor/{monitorId}/checks" +--- diff --git a/api-reference/endpoint/monitor-create.mdx b/api-reference/endpoint/monitor-create.mdx new file mode 100644 index 00000000..1cd06bcc --- /dev/null +++ b/api-reference/endpoint/monitor-create.mdx @@ -0,0 +1,4 @@ +--- +title: "Create Monitor" +openapi: "/api-reference/v2-openapi.json POST /monitor" +--- diff --git a/api-reference/endpoint/monitor-delete.mdx b/api-reference/endpoint/monitor-delete.mdx new file mode 100644 index 00000000..acb9c2f0 --- /dev/null +++ b/api-reference/endpoint/monitor-delete.mdx @@ -0,0 +1,4 @@ +--- +title: "Delete Monitor" +openapi: "/api-reference/v2-openapi.json DELETE /monitor/{monitorId}" +--- diff --git a/api-reference/endpoint/monitor-get.mdx b/api-reference/endpoint/monitor-get.mdx new file mode 100644 index 00000000..749d13cf --- /dev/null +++ b/api-reference/endpoint/monitor-get.mdx @@ -0,0 +1,4 @@ +--- +title: "Get Monitor" +openapi: "/api-reference/v2-openapi.json GET /monitor/{monitorId}" +--- diff --git a/api-reference/endpoint/monitor-list.mdx b/api-reference/endpoint/monitor-list.mdx new file mode 100644 index 00000000..df2432d5 --- /dev/null +++ b/api-reference/endpoint/monitor-list.mdx @@ -0,0 +1,4 @@ +--- +title: "List Monitors" +openapi: "/api-reference/v2-openapi.json GET /monitor" +--- diff --git a/api-reference/endpoint/monitor-run.mdx b/api-reference/endpoint/monitor-run.mdx new file mode 100644 index 00000000..f47d5e8c --- /dev/null +++ b/api-reference/endpoint/monitor-run.mdx @@ -0,0 +1,4 @@ +--- +title: "Run Monitor" +openapi: "/api-reference/v2-openapi.json POST /monitor/{monitorId}/run" +--- diff --git a/api-reference/endpoint/monitor-update.mdx b/api-reference/endpoint/monitor-update.mdx new file mode 100644 index 00000000..569f271e --- /dev/null +++ b/api-reference/endpoint/monitor-update.mdx @@ -0,0 +1,4 @@ +--- +title: "Update Monitor" +openapi: "/api-reference/v2-openapi.json PATCH /monitor/{monitorId}" +--- diff --git a/api-reference/endpoint/webhook-monitor-check-completed.mdx b/api-reference/endpoint/webhook-monitor-check-completed.mdx new file mode 100644 index 00000000..c5ae94c3 --- /dev/null +++ b/api-reference/endpoint/webhook-monitor-check-completed.mdx @@ -0,0 +1,4 @@ +--- +title: "Monitor Check Completed" +openapi: "/api-reference/webhooks-openapi.json webhook monitorCheckCompleted" +--- diff --git a/api-reference/v2-openapi.json b/api-reference/v2-openapi.json index 49989a5c..f874991d 100644 --- a/api-reference/v2-openapi.json +++ b/api-reference/v2-openapi.json @@ -16,6 +16,391 @@ } ], "paths": { + "/monitor": { + "post": { + "summary": "Create a monitor", + "operationId": "createMonitor", + "tags": ["Monitoring"], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitorCreateRequest" + }, + "examples": { + "scrapeMonitor": { + "summary": "Scrape a URL every 30 minutes", + "value": { + "name": "Blog monitor", + "schedule": { + "text": "every 30 minutes", + "timezone": "UTC" + }, + "notification": { + "email": { + "enabled": true, + "recipients": ["alerts@example.com"], + "includeDiffs": true + } + }, + "targets": [ + { + "type": "scrape", + "urls": ["https://example.com/blog"] + } + ] + } + }, + "crawlMonitor": { + "summary": "Crawl a site on a cron schedule", + "value": { + "name": "Docs monitor", + "schedule": { + "cron": "7-59/15 * * * *", + "timezone": "UTC" + }, + "webhook": { + "url": "https://example.com/webhooks/firecrawl", + "events": ["monitor.check.completed"] + }, + "targets": [ + { + "type": "crawl", + "url": "https://example.com/docs", + "crawlOptions": { + "limit": 100 + } + } + ] + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Monitor created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitorResponse" + } + } + } + }, + "400": { + "description": "Invalid monitor request" + } + } + }, + "get": { + "summary": "List monitors", + "operationId": "listMonitors", + "tags": ["Monitoring"], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25 + } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "default": 0 + } + } + ], + "responses": { + "200": { + "description": "List of monitors", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitorListResponse" + } + } + } + } + } + } + }, + "/monitor/{monitorId}": { + "get": { + "summary": "Get a monitor", + "operationId": "getMonitor", + "tags": ["Monitoring"], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/MonitorId" + } + ], + "responses": { + "200": { + "description": "Monitor details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitorResponse" + } + } + } + }, + "404": { + "description": "Monitor not found" + } + } + }, + "patch": { + "summary": "Update a monitor", + "operationId": "updateMonitor", + "tags": ["Monitoring"], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/MonitorId" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitorUpdateRequest" + }, + "example": { + "schedule": { + "text": "every 15 minutes starting at :07" + }, + "status": "active" + } + } + } + }, + "responses": { + "200": { + "description": "Monitor updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitorResponse" + } + } + } + }, + "404": { + "description": "Monitor not found" + } + } + }, + "delete": { + "summary": "Delete a monitor", + "operationId": "deleteMonitor", + "tags": ["Monitoring"], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/MonitorId" + } + ], + "responses": { + "200": { + "description": "Monitor deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } + }, + "404": { + "description": "Monitor not found" + } + } + } + }, + "/monitor/{monitorId}/run": { + "post": { + "summary": "Run a monitor", + "operationId": "runMonitor", + "tags": ["Monitoring"], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/MonitorId" + } + ], + "responses": { + "200": { + "description": "Monitor check queued", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitorRunResponse" + } + } + } + }, + "409": { + "description": "A monitor check is already running" + } + } + } + }, + "/monitor/{monitorId}/checks": { + "get": { + "summary": "List monitor checks", + "operationId": "listMonitorChecks", + "tags": ["Monitoring"], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/MonitorId" + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25 + } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "default": 0 + } + } + ], + "responses": { + "200": { + "description": "Monitor checks", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitorCheckListResponse" + } + } + } + } + } + } + }, + "/monitor/{monitorId}/checks/{checkId}": { + "get": { + "summary": "Get a monitor check", + "operationId": "getMonitorCheck", + "tags": ["Monitoring"], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/MonitorId" + }, + { + "name": "checkId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The monitor check ID" + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 25 + } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 0, + "default": 0 + } + }, + { + "name": "status", + "in": "query", + "schema": { + "type": "string", + "enum": ["same", "new", "changed", "removed", "error"] + } + } + ], + "responses": { + "200": { + "description": "Monitor check details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitorCheckDetailResponse" + } + } + } + }, + "404": { + "description": "Monitor check not found" + } + } + } + }, "/scrape": { "post": { "summary": "Scrape a single URL and optionally extract information using an LLM", @@ -3641,7 +4026,390 @@ "scheme": "bearer" } }, + "parameters": { + "MonitorId": { + "name": "monitorId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The monitor ID" + } + }, "schemas": { + "SuccessResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + } + } + }, + "MonitorSchedule": { + "type": "object", + "description": "Schedule for monitor checks. Provide either `cron` or `text`.", + "properties": { + "cron": { + "type": "string", + "description": "Five-field cron expression. Minimum interval is 15 minutes.", + "example": "*/30 * * * *" + }, + "text": { + "type": "string", + "description": "Natural language schedule. Supported examples include `every 30 minutes`, `every 15 minutes starting at :07`, `hourly`, `every 2 hours`, `daily`, `daily at 9:00`, and `weekly`.", + "example": "every 30 minutes" + }, + "timezone": { + "type": "string", + "default": "UTC", + "description": "IANA timezone for the schedule.", + "example": "UTC" + } + } + }, + "MonitorWebhook": { + "type": "object", + "description": "Webhook destination for monitor check completion events.", + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "The URL to send monitor webhooks to." + }, + "headers": { + "type": "object", + "description": "Headers to send to the webhook URL.", + "additionalProperties": { + "type": "string" + } + }, + "metadata": { + "type": "object", + "description": "Custom metadata included in webhook payloads.", + "additionalProperties": true + }, + "events": { + "type": "array", + "description": "Monitor webhook events to receive. Defaults to all monitor events.", + "items": { + "type": "string", + "enum": ["monitor.check.completed"] + } + } + }, + "required": ["url"] + }, + "MonitorNotification": { + "type": "object", + "properties": { + "email": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "recipients": { + "type": "array", + "maxItems": 25, + "items": { + "type": "string", + "format": "email" + } + }, + "includeDiffs": { + "type": "boolean", + "default": false, + "description": "Include changed page details in email summaries." + } + } + } + } + }, + "MonitorTarget": { + "oneOf": [ + { + "type": "object", + "title": "Scrape target", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "Optional stable ID for this target. Generated if omitted." + }, + "type": { + "type": "string", + "enum": ["scrape"] + }, + "urls": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "format": "uri" + } + }, + "scrapeOptions": { + "$ref": "#/components/schemas/ScrapeOptions" + } + }, + "required": ["type", "urls"] + }, + { + "type": "object", + "title": "Crawl target", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "Optional stable ID for this target. Generated if omitted." + }, + "type": { + "type": "string", + "enum": ["crawl"] + }, + "url": { + "type": "string", + "format": "uri" + }, + "crawlOptions": { + "type": "object", + "description": "Crawl options such as `limit`, `maxDepth`, `includePaths`, and `excludePaths`." + }, + "scrapeOptions": { + "$ref": "#/components/schemas/ScrapeOptions" + } + }, + "required": ["type", "url"] + } + ] + }, + "MonitorCreateRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 256 + }, + "schedule": { + "$ref": "#/components/schemas/MonitorSchedule" + }, + "webhook": { + "$ref": "#/components/schemas/MonitorWebhook" + }, + "notification": { + "$ref": "#/components/schemas/MonitorNotification" + }, + "targets": { + "type": "array", + "minItems": 1, + "maxItems": 50, + "items": { + "$ref": "#/components/schemas/MonitorTarget" + } + }, + "retentionDays": { + "type": "integer", + "minimum": 1, + "maximum": 365, + "default": 30 + } + }, + "required": ["name", "schedule", "targets"] + }, + "MonitorUpdateRequest": { + "type": "object", + "description": "Partial monitor update payload. Include at least one field.", + "properties": { + "name": { + "type": "string", + "maxLength": 256 + }, + "schedule": { + "$ref": "#/components/schemas/MonitorSchedule" + }, + "webhook": { + "$ref": "#/components/schemas/MonitorWebhook" + }, + "notification": { + "$ref": "#/components/schemas/MonitorNotification" + }, + "targets": { + "type": "array", + "minItems": 1, + "maxItems": 50, + "items": { + "$ref": "#/components/schemas/MonitorTarget" + } + }, + "retentionDays": { + "type": "integer", + "minimum": 1, + "maximum": 365 + }, + "status": { + "type": "string", + "enum": ["active", "paused"] + } + } + }, + "MonitorSummary": { + "type": "object", + "properties": { + "totalPages": { "type": "integer" }, + "same": { "type": "integer" }, + "changed": { "type": "integer" }, + "new": { "type": "integer" }, + "removed": { "type": "integer" }, + "error": { "type": "integer" } + } + }, + "Monitor": { + "type": "object", + "properties": { + "id": { "type": "string", "format": "uuid" }, + "name": { "type": "string" }, + "status": { "type": "string", "enum": ["active", "paused", "deleted"] }, + "schedule": { + "type": "object", + "properties": { + "cron": { "type": "string" }, + "timezone": { "type": "string" } + } + }, + "nextRunAt": { "type": "string", "format": "date-time", "nullable": true }, + "lastRunAt": { "type": "string", "format": "date-time", "nullable": true }, + "currentCheckId": { "type": "string", "format": "uuid", "nullable": true }, + "targets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MonitorTarget" + } + }, + "webhook": { + "$ref": "#/components/schemas/MonitorWebhook" + }, + "notification": { + "$ref": "#/components/schemas/MonitorNotification" + }, + "retentionDays": { "type": "integer" }, + "estimatedCreditsPerMonth": { "type": "integer", "nullable": true }, + "lastCheckSummary": { + "$ref": "#/components/schemas/MonitorSummary" + }, + "createdAt": { "type": "string", "format": "date-time" }, + "updatedAt": { "type": "string", "format": "date-time" } + } + }, + "MonitorCheck": { + "type": "object", + "properties": { + "id": { "type": "string", "format": "uuid" }, + "monitorId": { "type": "string", "format": "uuid" }, + "status": { + "type": "string", + "enum": ["queued", "running", "completed", "failed", "partial", "skipped_overlap"] + }, + "trigger": { "type": "string", "enum": ["scheduled", "manual"] }, + "scheduledFor": { "type": "string", "format": "date-time", "nullable": true }, + "startedAt": { "type": "string", "format": "date-time", "nullable": true }, + "finishedAt": { "type": "string", "format": "date-time", "nullable": true }, + "estimatedCredits": { "type": "integer", "nullable": true }, + "reservedCredits": { "type": "integer", "nullable": true }, + "actualCredits": { "type": "integer", "nullable": true }, + "billingStatus": { "type": "string" }, + "summary": { "$ref": "#/components/schemas/MonitorSummary" }, + "targetResults": { "type": "array", "nullable": true }, + "notificationStatus": { "type": "object", "nullable": true }, + "error": { "type": "string", "nullable": true }, + "createdAt": { "type": "string", "format": "date-time" }, + "updatedAt": { "type": "string", "format": "date-time" } + } + }, + "MonitorCheckPage": { + "type": "object", + "properties": { + "id": { "type": "string", "format": "uuid" }, + "targetId": { "type": "string" }, + "url": { "type": "string", "format": "uri" }, + "status": { "type": "string", "enum": ["same", "new", "changed", "removed", "error"] }, + "previousScrapeId": { "type": "string", "format": "uuid", "nullable": true }, + "currentScrapeId": { "type": "string", "format": "uuid", "nullable": true }, + "statusCode": { "type": "integer", "nullable": true }, + "error": { "type": "string", "nullable": true }, + "metadata": { "type": "object", "nullable": true }, + "diff": { + "type": "object", + "nullable": true, + "description": "Inline diff artifact when available.", + "properties": { + "text": { "type": "string" }, + "json": { "type": "object" } + } + }, + "createdAt": { "type": "string", "format": "date-time" } + } + }, + "MonitorResponse": { + "type": "object", + "properties": { + "success": { "type": "boolean" }, + "data": { "$ref": "#/components/schemas/Monitor" } + } + }, + "MonitorListResponse": { + "type": "object", + "properties": { + "success": { "type": "boolean" }, + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/Monitor" } + } + } + }, + "MonitorRunResponse": { + "type": "object", + "properties": { + "success": { "type": "boolean" }, + "id": { "type": "string", "format": "uuid" }, + "data": { "$ref": "#/components/schemas/MonitorCheck" } + } + }, + "MonitorCheckListResponse": { + "type": "object", + "properties": { + "success": { "type": "boolean" }, + "data": { + "type": "array", + "items": { "$ref": "#/components/schemas/MonitorCheck" } + } + } + }, + "MonitorCheckDetailResponse": { + "type": "object", + "properties": { + "success": { "type": "boolean" }, + "data": { + "allOf": [ + { "$ref": "#/components/schemas/MonitorCheck" }, + { + "type": "object", + "properties": { + "pages": { + "type": "array", + "items": { "$ref": "#/components/schemas/MonitorCheckPage" } + }, + "pageLimit": { "type": "integer" }, + "pageOffset": { "type": "integer" } + } + } + ] + } + } + }, "Formats": { "type": "array", "items": { diff --git a/api-reference/webhooks-openapi.json b/api-reference/webhooks-openapi.json index 2598dc6b..815598c6 100644 --- a/api-reference/webhooks-openapi.json +++ b/api-reference/webhooks-openapi.json @@ -912,6 +912,102 @@ } } } + }, + "monitorCheckCompleted": { + "post": { + "summary": "Monitor Check Completed", + "operationId": "monitorCheckCompleted", + "tags": [ + "Monitor" + ], + "parameters": [ + { + "$ref": "#/components/parameters/FirecrawlSignature" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "success", + "type", + "id", + "webhookId", + "data" + ], + "properties": { + "success": { + "type": "boolean", + "description": "`true` when the check completed without page errors, otherwise `false`." + }, + "type": { + "type": "string", + "description": "The event type.", + "const": "monitor.check.completed" + }, + "id": { + "type": "string", + "format": "uuid", + "description": "The monitor check ID." + }, + "webhookId": { + "type": "string", + "format": "uuid", + "description": "Unique identifier for this webhook delivery." + }, + "data": { + "$ref": "#/components/schemas/MonitorCheckCompletedData" + }, + "error": { + "type": "string", + "description": "Error message when the check failed." + }, + "metadata": { + "$ref": "#/components/schemas/WebhookMetadata" + } + } + }, + "example": { + "success": true, + "type": "monitor.check.completed", + "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "webhookId": "f1e2d3c4-0001-0000-0000-000000000000", + "data": { + "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", + "checkId": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "status": "completed", + "summary": { + "totalPages": 2, + "same": 1, + "changed": 1, + "new": 0, + "removed": 0, + "error": 0 + }, + "pages": [ + { + "url": "https://example.com/blog", + "status": "changed", + "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", + "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", + "error": null + } + ] + }, + "metadata": {} + } + } + } + }, + "responses": { + "200": { + "description": "Return any `2xx` status code to acknowledge receipt." + } + } + } } }, "components": { @@ -985,6 +1081,97 @@ } } } + }, + "MonitorCheckCompletedData": { + "type": "object", + "required": [ + "monitorId", + "checkId", + "status", + "summary" + ], + "properties": { + "monitorId": { + "type": "string", + "format": "uuid", + "description": "The monitor ID." + }, + "checkId": { + "type": "string", + "format": "uuid", + "description": "The monitor check ID." + }, + "status": { + "type": "string", + "enum": [ + "completed", + "partial", + "failed", + "skipped_overlap" + ], + "description": "Final status of the monitor check." + }, + "summary": { + "type": "object", + "required": [ + "totalPages", + "same", + "changed", + "new", + "removed", + "error" + ], + "properties": { + "totalPages": { "type": "integer" }, + "same": { "type": "integer" }, + "changed": { "type": "integer" }, + "new": { "type": "integer" }, + "removed": { "type": "integer" }, + "error": { "type": "integer" } + } + }, + "pages": { + "type": "array", + "description": "Up to 100 page-level results for the check.", + "items": { + "type": "object", + "required": [ + "url", + "status" + ], + "properties": { + "url": { + "type": "string", + "format": "uri" + }, + "status": { + "type": "string", + "enum": [ + "same", + "new", + "changed", + "removed", + "error" + ] + }, + "previousScrapeId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "currentScrapeId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "error": { + "type": "string", + "nullable": true + } + } + } + } + } } } } diff --git a/docs.json b/docs.json index 6ec9b7ab..af66c2e7 100755 --- a/docs.json +++ b/docs.json @@ -82,6 +82,7 @@ "features/parse", "features/map", "features/crawl", + "features/monitoring", { "group": "Agent (Research Preview)", "icon": "robot", @@ -368,6 +369,19 @@ "api-reference/endpoint/crawl-active" ] }, + { + "group": "Monitor Endpoints", + "pages": [ + "api-reference/endpoint/monitor-create", + "api-reference/endpoint/monitor-list", + "api-reference/endpoint/monitor-get", + "api-reference/endpoint/monitor-update", + "api-reference/endpoint/monitor-delete", + "api-reference/endpoint/monitor-run", + "api-reference/endpoint/monitor-checks-list", + "api-reference/endpoint/monitor-check-get" + ] + }, { "group": "Agent Endpoints", "pages": [ @@ -413,6 +427,12 @@ "api-reference/endpoint/webhook-batch-scrape-completed" ] }, + { + "group": "Monitor", + "pages": [ + "api-reference/endpoint/webhook-monitor-check-completed" + ] + }, { "group": "Agent", "pages": [ diff --git a/features/monitoring.mdx b/features/monitoring.mdx new file mode 100644 index 00000000..070ade77 --- /dev/null +++ b/features/monitoring.mdx @@ -0,0 +1,277 @@ +--- +title: "Monitoring" +description: "Schedule recurring scrapes and crawls, detect content changes, and receive webhook or email notifications" +og:title: "Monitoring | Firecrawl" +og:description: "Schedule recurring scrapes and crawls, detect content changes, and receive webhook or email notifications" +icon: "radar" +--- + +Firecrawl monitors run recurring scrapes or crawls and compare each result against the last retained snapshot. Use monitors to track product pages, docs, blogs, changelogs, competitor sites, or any page where changes matter. + +Each check records page-level results as `same`, `new`, `changed`, `removed`, or `error`. You can receive a webhook for every completed check, email summaries when changes or errors happen, or both. + + + Monitoring requires retained snapshots and diffs. It is not available for zero data retention teams. + + +## Create a monitor + +Create a scrape monitor for one or more explicit URLs: + +```bash +curl -X POST "https://api.firecrawl.dev/v2/monitor" \ + -H "Authorization: Bearer $FIRECRAWL_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Blog monitor", + "schedule": { + "text": "every 30 minutes", + "timezone": "UTC" + }, + "notification": { + "email": { + "enabled": true, + "recipients": ["alerts@example.com"], + "includeDiffs": true + } + }, + "targets": [ + { + "type": "scrape", + "urls": ["https://example.com/blog"] + } + ] + }' +``` + +Create a crawl monitor to diff every page discovered by a crawl on each check: + +```bash +curl -X POST "https://api.firecrawl.dev/v2/monitor" \ + -H "Authorization: Bearer $FIRECRAWL_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Docs monitor", + "schedule": { + "cron": "7-59/15 * * * *", + "timezone": "UTC" + }, + "webhook": { + "url": "https://example.com/webhooks/firecrawl", + "events": ["monitor.check.completed"] + }, + "targets": [ + { + "type": "crawl", + "url": "https://example.com/docs", + "crawlOptions": { + "limit": 100, + "maxDepth": 3 + } + } + ] + }' +``` + +## Schedules + +Schedules can be provided as cron or as simple natural language text. + +```json +{ + "schedule": { + "cron": "*/30 * * * *", + "timezone": "UTC" + } +} +``` + +```json +{ + "schedule": { + "text": "every 30 minutes", + "timezone": "UTC" + } +} +``` + +Supported natural language examples: + +- `every 30 minutes` +- `every 15 minutes starting at :07` +- `hourly` +- `every 2 hours` +- `daily` +- `daily at 9:00` +- `weekly` + +The minimum interval is 15 minutes. API responses always return the normalized cron expression. + +## Targets + +Monitors support two target types: + +- `scrape`: Runs one scrape per URL in `urls`. +- `crawl`: Runs a full crawl for `url` on each check, then diffs all discovered pages. + +Target scrape options are passed through to the underlying scrape jobs. Monitor-triggered scrapes default `maxAge` to `0`, so each check performs a fresh scrape unless you explicitly set a different `maxAge`. + +```json +{ + "type": "scrape", + "urls": ["https://example.com/pricing"], + "scrapeOptions": { + "formats": ["markdown"], + "maxAge": 0 + } +} +``` + +For crawl targets, use `crawlOptions` for crawl behavior and `scrapeOptions` for each page scrape: + +```json +{ + "type": "crawl", + "url": "https://example.com/docs", + "crawlOptions": { + "limit": 100, + "includePaths": ["/docs"] + }, + "scrapeOptions": { + "formats": ["markdown"] + } +} +``` + +## Notifications + +### Webhooks + +When a monitor has a `webhook`, Firecrawl sends `monitor.check.completed` after each completed check. The webhook contains the check summary and up to 100 page-level results. + +```json +{ + "webhook": { + "url": "https://example.com/webhooks/firecrawl", + "headers": { + "Authorization": "Bearer your-secret" + }, + "metadata": { + "environment": "production" + }, + "events": ["monitor.check.completed"] + } +} +``` + +Payload: + +```json +{ + "success": true, + "type": "monitor.check.completed", + "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "webhookId": "f1e2d3c4-0001-0000-0000-000000000000", + "data": { + "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", + "checkId": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "status": "completed", + "summary": { + "totalPages": 2, + "same": 1, + "changed": 1, + "new": 0, + "removed": 0, + "error": 0 + }, + "pages": [ + { + "url": "https://example.com/blog", + "status": "changed", + "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", + "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", + "error": null + } + ] + }, + "metadata": { + "environment": "production" + } +} +``` + +`success` is `true` when the check completed without page errors. It is `false` for failed or partial checks, and `error` contains the failure reason when available. + +### Email + +Email summaries are sent only when a check has changed, new, removed, or errored pages. + +```json +{ + "notification": { + "email": { + "enabled": true, + "recipients": ["alerts@example.com"], + "includeDiffs": true + } + } +} +``` + +If `recipients` is omitted, Firecrawl sends to team members who are eligible for system alert emails. + +## Check results + +Use `GET /v2/monitor/{monitorId}/checks` to list checks and `GET /v2/monitor/{monitorId}/checks/{checkId}` to inspect a check. + +```bash +curl "https://api.firecrawl.dev/v2/monitor/$MONITOR_ID/checks/$CHECK_ID?limit=25" \ + -H "Authorization: Bearer $FIRECRAWL_API_KEY" +``` + +The check detail response includes summary counts and a paginated `pages` array. Changed pages include inline diff data when available. + +```json +{ + "success": true, + "data": { + "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", + "status": "completed", + "summary": { + "totalPages": 1, + "same": 0, + "changed": 1, + "new": 0, + "removed": 0, + "error": 0 + }, + "pages": [ + { + "url": "https://example.com/blog", + "status": "changed", + "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", + "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", + "diff": { + "text": "...", + "json": {} + } + } + ], + "pageLimit": 25, + "pageOffset": 0 + } +} +``` + +## API reference + +- [Create monitor](/api-reference/endpoint/monitor-create) +- [List monitors](/api-reference/endpoint/monitor-list) +- [Get monitor](/api-reference/endpoint/monitor-get) +- [Update monitor](/api-reference/endpoint/monitor-update) +- [Delete monitor](/api-reference/endpoint/monitor-delete) +- [Run monitor](/api-reference/endpoint/monitor-run) +- [List monitor checks](/api-reference/endpoint/monitor-checks-list) +- [Get monitor check](/api-reference/endpoint/monitor-check-get) +- [Monitor webhook payload](/api-reference/endpoint/webhook-monitor-check-completed) diff --git a/webhooks/events.mdx b/webhooks/events.mdx index 16a37e9f..5834142b 100644 --- a/webhooks/events.mdx +++ b/webhooks/events.mdx @@ -26,6 +26,7 @@ Firecrawl sends webhook events at each stage of a job's lifecycle, so you can tr | `agent.completed` | Agent finishes successfully | | `agent.failed` | Agent encounters an error | | `agent.cancelled` | Agent job is cancelled by the user | +| `monitor.check.completed` | Monitor check finishes and page-level changes are available | ## Payload Structure @@ -46,7 +47,7 @@ All webhook events share this structure: | `success` | boolean | Whether the operation succeeded | | `type` | string | Event type (e.g. `crawl.page`) | | `id` | string | Job ID | -| `data` | array | Event-specific data (see examples below) | +| `data` | array or object | Event-specific data (see examples below) | | `metadata` | object | Custom metadata from your webhook config | | `error` | string | Error message (when `success` is `false`) | @@ -172,6 +173,46 @@ Sent when all URLs in the batch have been processed. } ``` +## Monitor Events + +### `monitor.check.completed` + +Sent when a monitor check finishes. The `data` object contains summary counts and up to 100 page-level results. + +```json +{ + "success": true, + "type": "monitor.check.completed", + "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "webhookId": "f1e2d3c4-0001-0000-0000-000000000000", + "data": { + "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", + "checkId": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "status": "completed", + "summary": { + "totalPages": 2, + "same": 1, + "changed": 1, + "new": 0, + "removed": 0, + "error": 0 + }, + "pages": [ + { + "url": "https://example.com/blog", + "status": "changed", + "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", + "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", + "error": null + } + ] + }, + "metadata": {} +} +``` + +`success` is `true` when the check completed without page errors. For partial or failed checks, `success` is `false` and `error` may contain a message. + ## Extract Events ### `extract.started` diff --git a/webhooks/overview.mdx b/webhooks/overview.mdx index cb99b53e..d1728b95 100644 --- a/webhooks/overview.mdx +++ b/webhooks/overview.mdx @@ -10,7 +10,7 @@ import CrawlWebhookCURL from "/snippets/v2/crawl-webhook/base/curl.mdx"; import BatchScrapeWebhookCURL from "/snippets/v2/batch-scrape-webhook/base/curl.mdx"; import WebhookConfigBasic from "/snippets/v2/webhook-config/basic/json.mdx"; -Get notified the moment a crawl, batch scrape, extract, or agent job starts, progresses, or finishes. Instead of polling for status, you provide an HTTPS endpoint and Firecrawl delivers events to it in real time. +Get notified the moment a crawl, batch scrape, extract, agent job, or monitor check starts, progresses, or finishes. Instead of polling for status, you provide an HTTPS endpoint and Firecrawl delivers events to it in real time. ## Supported Operations @@ -20,6 +20,7 @@ Get notified the moment a crawl, batch scrape, extract, or agent job starts, pro | Batch Scrape | `started`, `page`, `completed` | | Extract | `started`, `completed`, `failed` | | Agent | `started`, `action`, `completed`, `failed`, `cancelled` | +| Monitor | `check.completed` | See [Event Types](/webhooks/events) for full payload details and examples. From b2dd8982ca244eeb1856ea01214f4ce611065135 Mon Sep 17 00:00:00 2001 From: mogery Date: Tue, 5 May 2026 21:35:16 +0200 Subject: [PATCH 2/4] Document monitor page webhooks Co-authored-by: Cursor --- .../endpoint/webhook-monitor-page.mdx | 4 + api-reference/v2-openapi.json | 6 +- api-reference/webhooks-openapi.json | 133 ++++++++++++++++++ docs.json | 1 + features/monitoring.mdx | 39 ++++- webhooks/events.mdx | 24 ++++ 6 files changed, 198 insertions(+), 9 deletions(-) create mode 100644 api-reference/endpoint/webhook-monitor-page.mdx diff --git a/api-reference/endpoint/webhook-monitor-page.mdx b/api-reference/endpoint/webhook-monitor-page.mdx new file mode 100644 index 00000000..6c7d946f --- /dev/null +++ b/api-reference/endpoint/webhook-monitor-page.mdx @@ -0,0 +1,4 @@ +--- +title: "Monitor Page" +openapi: "/api-reference/webhooks-openapi.json webhook monitorPage" +--- diff --git a/api-reference/v2-openapi.json b/api-reference/v2-openapi.json index f874991d..13eb6a9a 100644 --- a/api-reference/v2-openapi.json +++ b/api-reference/v2-openapi.json @@ -67,7 +67,7 @@ }, "webhook": { "url": "https://example.com/webhooks/firecrawl", - "events": ["monitor.check.completed"] + "events": ["monitor.page", "monitor.check.completed"] }, "targets": [ { @@ -4072,7 +4072,7 @@ }, "MonitorWebhook": { "type": "object", - "description": "Webhook destination for monitor check completion events.", + "description": "Webhook destination for monitor page and check completion events.", "properties": { "url": { "type": "string", @@ -4096,7 +4096,7 @@ "description": "Monitor webhook events to receive. Defaults to all monitor events.", "items": { "type": "string", - "enum": ["monitor.check.completed"] + "enum": ["monitor.page", "monitor.check.completed"] } } }, diff --git a/api-reference/webhooks-openapi.json b/api-reference/webhooks-openapi.json index 815598c6..c3c388c4 100644 --- a/api-reference/webhooks-openapi.json +++ b/api-reference/webhooks-openapi.json @@ -913,6 +913,89 @@ } } }, + "monitorPage": { + "post": { + "summary": "Monitor Page", + "operationId": "monitorPage", + "tags": [ + "Monitor" + ], + "parameters": [ + { + "$ref": "#/components/parameters/FirecrawlSignature" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "success", + "type", + "id", + "webhookId", + "data" + ], + "properties": { + "success": { + "type": "boolean", + "description": "`true` when the page scrape completed without an error." + }, + "type": { + "type": "string", + "description": "The event type.", + "const": "monitor.page" + }, + "id": { + "type": "string", + "format": "uuid", + "description": "The monitor check ID." + }, + "webhookId": { + "type": "string", + "format": "uuid", + "description": "Unique identifier for this webhook delivery." + }, + "data": { + "$ref": "#/components/schemas/MonitorPageData" + }, + "error": { + "type": "string", + "description": "Error message when the monitored page failed." + }, + "metadata": { + "$ref": "#/components/schemas/WebhookMetadata" + } + } + }, + "example": { + "success": true, + "type": "monitor.page", + "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "webhookId": "f1e2d3c4-0000-0000-0000-000000000000", + "data": { + "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", + "checkId": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "url": "https://example.com/blog", + "status": "changed", + "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", + "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", + "error": null + }, + "metadata": {} + } + } + } + }, + "responses": { + "200": { + "description": "Return any `2xx` status code to acknowledge receipt." + } + } + } + }, "monitorCheckCompleted": { "post": { "summary": "Monitor Check Completed", @@ -1082,6 +1165,56 @@ } } }, + "MonitorPageData": { + "type": "object", + "required": [ + "monitorId", + "checkId", + "url", + "status" + ], + "properties": { + "monitorId": { + "type": "string", + "format": "uuid", + "description": "The monitor ID." + }, + "checkId": { + "type": "string", + "format": "uuid", + "description": "The monitor check ID." + }, + "url": { + "type": "string", + "format": "uri", + "description": "The page URL." + }, + "status": { + "type": "string", + "enum": [ + "same", + "new", + "changed", + "error" + ], + "description": "Page-level status for this scrape completion." + }, + "previousScrapeId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "currentScrapeId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "error": { + "type": "string", + "nullable": true + } + } + }, "MonitorCheckCompletedData": { "type": "object", "required": [ diff --git a/docs.json b/docs.json index af66c2e7..46d73861 100755 --- a/docs.json +++ b/docs.json @@ -430,6 +430,7 @@ { "group": "Monitor", "pages": [ + "api-reference/endpoint/webhook-monitor-page", "api-reference/endpoint/webhook-monitor-check-completed" ] }, diff --git a/features/monitoring.mdx b/features/monitoring.mdx index 070ade77..538e51d4 100644 --- a/features/monitoring.mdx +++ b/features/monitoring.mdx @@ -8,7 +8,7 @@ icon: "radar" Firecrawl monitors run recurring scrapes or crawls and compare each result against the last retained snapshot. Use monitors to track product pages, docs, blogs, changelogs, competitor sites, or any page where changes matter. -Each check records page-level results as `same`, `new`, `changed`, `removed`, or `error`. You can receive a webhook for every completed check, email summaries when changes or errors happen, or both. +Each check records page-level results as `same`, `new`, `changed`, `removed`, or `error`. You can receive a webhook as each monitored page finishes, a webhook for every completed check, email summaries when changes or errors happen, or any combination of those notifications. Monitoring requires retained snapshots and diffs. It is not available for zero data retention teams. @@ -58,7 +58,7 @@ curl -X POST "https://api.firecrawl.dev/v2/monitor" \ }, "webhook": { "url": "https://example.com/webhooks/firecrawl", - "events": ["monitor.check.completed"] + "events": ["monitor.page", "monitor.check.completed"] }, "targets": [ { @@ -147,7 +147,10 @@ For crawl targets, use `crawlOptions` for crawl behavior and `scrapeOptions` for ### Webhooks -When a monitor has a `webhook`, Firecrawl sends `monitor.check.completed` after each completed check. The webhook contains the check summary and up to 100 page-level results. +When a monitor has a `webhook`, Firecrawl can send two monitor events: + +- `monitor.page`: Sent as each monitored scrape finishes in the scrape worker. +- `monitor.check.completed`: Sent after the full check is reconciled. Includes summary counts and up to 100 page-level results. ```json { @@ -159,12 +162,35 @@ When a monitor has a `webhook`, Firecrawl sends `monitor.check.completed` after "metadata": { "environment": "production" }, - "events": ["monitor.check.completed"] + "events": ["monitor.page", "monitor.check.completed"] + } +} +``` + +`monitor.page` payload: + +```json +{ + "success": true, + "type": "monitor.page", + "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "webhookId": "f1e2d3c4-0000-0000-0000-000000000000", + "data": { + "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", + "checkId": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "url": "https://example.com/blog", + "status": "changed", + "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", + "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", + "error": null + }, + "metadata": { + "environment": "production" } } ``` -Payload: +`monitor.check.completed` payload: ```json { @@ -274,4 +300,5 @@ The check detail response includes summary counts and a paginated `pages` array. - [Run monitor](/api-reference/endpoint/monitor-run) - [List monitor checks](/api-reference/endpoint/monitor-checks-list) - [Get monitor check](/api-reference/endpoint/monitor-check-get) -- [Monitor webhook payload](/api-reference/endpoint/webhook-monitor-check-completed) +- [Monitor page webhook payload](/api-reference/endpoint/webhook-monitor-page) +- [Monitor check completed webhook payload](/api-reference/endpoint/webhook-monitor-check-completed) diff --git a/webhooks/events.mdx b/webhooks/events.mdx index 5834142b..95350fdf 100644 --- a/webhooks/events.mdx +++ b/webhooks/events.mdx @@ -26,6 +26,7 @@ Firecrawl sends webhook events at each stage of a job's lifecycle, so you can tr | `agent.completed` | Agent finishes successfully | | `agent.failed` | Agent encounters an error | | `agent.cancelled` | Agent job is cancelled by the user | +| `monitor.page` | A monitored page scrape finishes | | `monitor.check.completed` | Monitor check finishes and page-level changes are available | ## Payload Structure @@ -175,6 +176,29 @@ Sent when all URLs in the batch have been processed. ## Monitor Events +### `monitor.page` + +Sent as each monitored page scrape finishes. This event is emitted from the scrape worker path, so it arrives before the full monitor check is reconciled. + +```json +{ + "success": true, + "type": "monitor.page", + "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "webhookId": "f1e2d3c4-0000-0000-0000-000000000000", + "data": { + "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", + "checkId": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "url": "https://example.com/blog", + "status": "changed", + "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", + "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", + "error": null + }, + "metadata": {} +} +``` + ### `monitor.check.completed` Sent when a monitor check finishes. The `data` object contains summary counts and up to 100 page-level results. From b372a19837b9ed7e19aef96a2e0a2a7025926b18 Mon Sep 17 00:00:00 2001 From: mogery Date: Tue, 5 May 2026 21:59:55 +0200 Subject: [PATCH 3/4] changes --- api-reference/v2-openapi.json | 17 +++++++--- api-reference/webhooks-openapi.json | 52 +---------------------------- features/monitoring.mdx | 19 +++-------- webhooks/events.mdx | 13 ++------ 4 files changed, 21 insertions(+), 80 deletions(-) diff --git a/api-reference/v2-openapi.json b/api-reference/v2-openapi.json index 13eb6a9a..925327f5 100644 --- a/api-reference/v2-openapi.json +++ b/api-reference/v2-openapi.json @@ -367,13 +367,14 @@ } }, { - "name": "offset", + "name": "skip", "in": "query", "schema": { "type": "integer", "minimum": 0, "default": 0 - } + }, + "description": "Number of page results to skip. Use the `next` URL from the previous response for pagination." }, { "name": "status", @@ -4392,6 +4393,11 @@ "type": "object", "properties": { "success": { "type": "boolean" }, + "next": { + "type": "string", + "nullable": true, + "description": "URL to fetch the next page of monitor check page results, if any." + }, "data": { "allOf": [ { "$ref": "#/components/schemas/MonitorCheck" }, @@ -4402,8 +4408,11 @@ "type": "array", "items": { "$ref": "#/components/schemas/MonitorCheckPage" } }, - "pageLimit": { "type": "integer" }, - "pageOffset": { "type": "integer" } + "next": { + "type": "string", + "nullable": true, + "description": "URL to fetch the next page of monitor check page results, if any." + } } } ] diff --git a/api-reference/webhooks-openapi.json b/api-reference/webhooks-openapi.json index c3c388c4..34c83e9c 100644 --- a/api-reference/webhooks-openapi.json +++ b/api-reference/webhooks-openapi.json @@ -1069,16 +1069,7 @@ "new": 0, "removed": 0, "error": 0 - }, - "pages": [ - { - "url": "https://example.com/blog", - "status": "changed", - "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", - "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", - "error": null - } - ] + } }, "metadata": {} } @@ -1262,47 +1253,6 @@ "removed": { "type": "integer" }, "error": { "type": "integer" } } - }, - "pages": { - "type": "array", - "description": "Up to 100 page-level results for the check.", - "items": { - "type": "object", - "required": [ - "url", - "status" - ], - "properties": { - "url": { - "type": "string", - "format": "uri" - }, - "status": { - "type": "string", - "enum": [ - "same", - "new", - "changed", - "removed", - "error" - ] - }, - "previousScrapeId": { - "type": "string", - "format": "uuid", - "nullable": true - }, - "currentScrapeId": { - "type": "string", - "format": "uuid", - "nullable": true - }, - "error": { - "type": "string", - "nullable": true - } - } - } } } } diff --git a/features/monitoring.mdx b/features/monitoring.mdx index 538e51d4..4c3d575b 100644 --- a/features/monitoring.mdx +++ b/features/monitoring.mdx @@ -150,7 +150,7 @@ For crawl targets, use `crawlOptions` for crawl behavior and `scrapeOptions` for When a monitor has a `webhook`, Firecrawl can send two monitor events: - `monitor.page`: Sent as each monitored scrape finishes in the scrape worker. -- `monitor.check.completed`: Sent after the full check is reconciled. Includes summary counts and up to 100 page-level results. +- `monitor.check.completed`: Sent after the full check is reconciled. Includes check status and summary counts. Use `monitor.page` events or the monitor check API for page-level results. ```json { @@ -209,16 +209,7 @@ When a monitor has a `webhook`, Firecrawl can send two monitor events: "new": 0, "removed": 0, "error": 0 - }, - "pages": [ - { - "url": "https://example.com/blog", - "status": "changed", - "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", - "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", - "error": null - } - ] + } }, "metadata": { "environment": "production" @@ -255,11 +246,12 @@ curl "https://api.firecrawl.dev/v2/monitor/$MONITOR_ID/checks/$CHECK_ID?limit=25 -H "Authorization: Bearer $FIRECRAWL_API_KEY" ``` -The check detail response includes summary counts and a paginated `pages` array. Changed pages include inline diff data when available. +The check detail response includes summary counts and a paginated `pages` array. Use the top-level `next` URL to fetch the next page of results, matching crawl pagination. Changed pages include inline diff data when available. ```json { "success": true, + "next": "https://api.firecrawl.dev/v2/monitor/019df960-06e7-7383-9d89-82c0113dc31a/checks/019df960-5f2a-75fb-a98b-bd2d32ca67d4?skip=25&limit=25", "data": { "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", @@ -284,8 +276,7 @@ The check detail response includes summary counts and a paginated `pages` array. } } ], - "pageLimit": 25, - "pageOffset": 0 + "next": "https://api.firecrawl.dev/v2/monitor/019df960-06e7-7383-9d89-82c0113dc31a/checks/019df960-5f2a-75fb-a98b-bd2d32ca67d4?skip=25&limit=25" } } ``` diff --git a/webhooks/events.mdx b/webhooks/events.mdx index 95350fdf..8f024fc2 100644 --- a/webhooks/events.mdx +++ b/webhooks/events.mdx @@ -201,7 +201,7 @@ Sent as each monitored page scrape finishes. This event is emitted from the scra ### `monitor.check.completed` -Sent when a monitor check finishes. The `data` object contains summary counts and up to 100 page-level results. +Sent when a monitor check finishes. The `data` object contains check status and summary counts. Page-level results are only sent through `monitor.page` events or returned from the monitor check API. ```json { @@ -220,16 +220,7 @@ Sent when a monitor check finishes. The `data` object contains summary counts an "new": 0, "removed": 0, "error": 0 - }, - "pages": [ - { - "url": "https://example.com/blog", - "status": "changed", - "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", - "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", - "error": null - } - ] + } }, "metadata": {} } From 5987a8ab9af68b7740efe0d0fd255f96a35916ea Mon Sep 17 00:00:00 2001 From: Nicolas <20311743+nickscamara@users.noreply.github.com> Date: Sun, 17 May 2026 12:14:56 -0400 Subject: [PATCH 4/4] Nick: --- api-reference/v2-openapi.json | 23 +- features/monitoring.mdx | 315 +++++++----------- snippets/v2/monitor/check/get/curl.mdx | 4 + snippets/v2/monitor/check/get/js.mdx | 21 ++ snippets/v2/monitor/check/get/python.mdx | 16 + snippets/v2/monitor/check/output/json.mdx | 54 +++ snippets/v2/monitor/check/output/markdown.mdx | 40 +++ snippets/v2/monitor/check/output/mixed.mdx | 43 +++ snippets/v2/monitor/create/crawl/curl.mdx | 26 ++ snippets/v2/monitor/create/crawl/js.mdx | 26 ++ snippets/v2/monitor/create/crawl/python.mdx | 26 ++ snippets/v2/monitor/create/json/curl.mdx | 53 +++ snippets/v2/monitor/create/json/js.mdx | 46 +++ snippets/v2/monitor/create/json/python.mdx | 48 +++ snippets/v2/monitor/create/output.mdx | 37 ++ snippets/v2/monitor/create/scrape/curl.mdx | 25 ++ snippets/v2/monitor/create/scrape/js.mdx | 25 ++ snippets/v2/monitor/create/scrape/python.mdx | 25 ++ snippets/v2/monitor/diff/json.mdx | 32 ++ snippets/v2/monitor/diff/markdown.mdx | 21 ++ snippets/v2/monitor/diff/mixed.mdx | 21 ++ .../notification/check-completed-payload.mdx | 24 ++ snippets/v2/monitor/notification/email.mdx | 11 + .../v2/monitor/notification/page-payload.mdx | 20 ++ snippets/v2/monitor/notification/webhook.mdx | 14 + snippets/v2/monitor/schedule/cron.mdx | 8 + snippets/v2/monitor/schedule/text.mdx | 8 + snippets/v2/monitor/target/crawl.mdx | 13 + snippets/v2/monitor/target/json-mixed.mdx | 30 ++ snippets/v2/monitor/target/json.mdx | 34 ++ snippets/v2/monitor/target/scrape.mdx | 10 + webhooks/events.mdx | 45 +-- 32 files changed, 900 insertions(+), 244 deletions(-) create mode 100644 snippets/v2/monitor/check/get/curl.mdx create mode 100644 snippets/v2/monitor/check/get/js.mdx create mode 100644 snippets/v2/monitor/check/get/python.mdx create mode 100644 snippets/v2/monitor/check/output/json.mdx create mode 100644 snippets/v2/monitor/check/output/markdown.mdx create mode 100644 snippets/v2/monitor/check/output/mixed.mdx create mode 100644 snippets/v2/monitor/create/crawl/curl.mdx create mode 100644 snippets/v2/monitor/create/crawl/js.mdx create mode 100644 snippets/v2/monitor/create/crawl/python.mdx create mode 100644 snippets/v2/monitor/create/json/curl.mdx create mode 100644 snippets/v2/monitor/create/json/js.mdx create mode 100644 snippets/v2/monitor/create/json/python.mdx create mode 100644 snippets/v2/monitor/create/output.mdx create mode 100644 snippets/v2/monitor/create/scrape/curl.mdx create mode 100644 snippets/v2/monitor/create/scrape/js.mdx create mode 100644 snippets/v2/monitor/create/scrape/python.mdx create mode 100644 snippets/v2/monitor/diff/json.mdx create mode 100644 snippets/v2/monitor/diff/markdown.mdx create mode 100644 snippets/v2/monitor/diff/mixed.mdx create mode 100644 snippets/v2/monitor/notification/check-completed-payload.mdx create mode 100644 snippets/v2/monitor/notification/email.mdx create mode 100644 snippets/v2/monitor/notification/page-payload.mdx create mode 100644 snippets/v2/monitor/notification/webhook.mdx create mode 100644 snippets/v2/monitor/schedule/cron.mdx create mode 100644 snippets/v2/monitor/schedule/text.mdx create mode 100644 snippets/v2/monitor/target/crawl.mdx create mode 100644 snippets/v2/monitor/target/json-mixed.mdx create mode 100644 snippets/v2/monitor/target/json.mdx create mode 100644 snippets/v2/monitor/target/scrape.mdx diff --git a/api-reference/v2-openapi.json b/api-reference/v2-openapi.json index 925327f5..d9ce020b 100644 --- a/api-reference/v2-openapi.json +++ b/api-reference/v2-openapi.json @@ -4345,10 +4345,27 @@ "diff": { "type": "object", "nullable": true, - "description": "Inline diff artifact when available.", + "description": "Inline diff artifact when the page changed. The shape depends on what the monitor's scrapeOptions.formats asked for. Markdown-only monitors populate both `text` (unified diff) and `json` (parseDiff AST). JSON-extraction monitors populate `json` as a per-field `{previous, current}` map keyed by JSON path. Mixed-mode monitors (`changeTracking` with both `json` and `git-diff` modes) populate both `text` (markdown sidecar) and `json` (per-field diff).", "properties": { - "text": { "type": "string" }, - "json": { "type": "object" } + "text": { + "type": "string", + "description": "Unified markdown diff. Present on markdown-only and mixed-mode monitors." + }, + "json": { + "type": "object", + "description": "For markdown-only monitors, a parseDiff AST `{ files: [...] }`. For JSON-extraction (and mixed-mode) monitors, a per-field `{ previous, current }` map keyed by the JSON path into the extraction (e.g. `plans[0].price`)." + } + } + }, + "snapshot": { + "type": "object", + "nullable": true, + "description": "Snapshot of the current JSON extraction at this run. Present on JSON-extraction and mixed-mode monitors; absent for markdown-only monitors.", + "properties": { + "json": { + "type": "object", + "description": "The full structured JSON extracted on this run, matching the schema/prompt declared on the target's `changeTracking` format." + } } }, "createdAt": { "type": "string", "format": "date-time" } diff --git a/features/monitoring.mdx b/features/monitoring.mdx index 4c3d575b..6dc2862d 100644 --- a/features/monitoring.mdx +++ b/features/monitoring.mdx @@ -6,6 +6,35 @@ og:description: "Schedule recurring scrapes and crawls, detect content changes, icon: "radar" --- +import CreateScrapeCURL from "/snippets/v2/monitor/create/scrape/curl.mdx"; +import CreateScrapeNode from "/snippets/v2/monitor/create/scrape/js.mdx"; +import CreateScrapePython from "/snippets/v2/monitor/create/scrape/python.mdx"; +import CreateCrawlCURL from "/snippets/v2/monitor/create/crawl/curl.mdx"; +import CreateCrawlNode from "/snippets/v2/monitor/create/crawl/js.mdx"; +import CreateCrawlPython from "/snippets/v2/monitor/create/crawl/python.mdx"; +import CreateJsonCURL from "/snippets/v2/monitor/create/json/curl.mdx"; +import CreateJsonNode from "/snippets/v2/monitor/create/json/js.mdx"; +import CreateJsonPython from "/snippets/v2/monitor/create/json/python.mdx"; +import CreateOutput from "/snippets/v2/monitor/create/output.mdx"; +import ScheduleCron from "/snippets/v2/monitor/schedule/cron.mdx"; +import ScheduleText from "/snippets/v2/monitor/schedule/text.mdx"; +import TargetScrape from "/snippets/v2/monitor/target/scrape.mdx"; +import TargetCrawl from "/snippets/v2/monitor/target/crawl.mdx"; +import TargetJsonMixed from "/snippets/v2/monitor/target/json-mixed.mdx"; +import WebhookConfig from "/snippets/v2/monitor/notification/webhook.mdx"; +import EmailConfig from "/snippets/v2/monitor/notification/email.mdx"; +import PagePayload from "/snippets/v2/monitor/notification/page-payload.mdx"; +import CheckCompletedPayload from "/snippets/v2/monitor/notification/check-completed-payload.mdx"; +import GetCheckCURL from "/snippets/v2/monitor/check/get/curl.mdx"; +import GetCheckNode from "/snippets/v2/monitor/check/get/js.mdx"; +import GetCheckPython from "/snippets/v2/monitor/check/get/python.mdx"; +import CheckOutputMarkdown from "/snippets/v2/monitor/check/output/markdown.mdx"; +import CheckOutputJson from "/snippets/v2/monitor/check/output/json.mdx"; +import CheckOutputMixed from "/snippets/v2/monitor/check/output/mixed.mdx"; +import DiffMarkdown from "/snippets/v2/monitor/diff/markdown.mdx"; +import DiffJson from "/snippets/v2/monitor/diff/json.mdx"; +import DiffMixed from "/snippets/v2/monitor/diff/mixed.mdx"; + Firecrawl monitors run recurring scrapes or crawls and compare each result against the last retained snapshot. Use monitors to track product pages, docs, blogs, changelogs, competitor sites, or any page where changes matter. Each check records page-level results as `same`, `new`, `changed`, `removed`, or `error`. You can receive a webhook as each monitored page finishes, a webhook for every completed check, email summaries when changes or errors happen, or any combination of those notifications. @@ -18,82 +47,32 @@ Each check records page-level results as `same`, `new`, `changed`, `removed`, or Create a scrape monitor for one or more explicit URLs: -```bash -curl -X POST "https://api.firecrawl.dev/v2/monitor" \ - -H "Authorization: Bearer $FIRECRAWL_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "Blog monitor", - "schedule": { - "text": "every 30 minutes", - "timezone": "UTC" - }, - "notification": { - "email": { - "enabled": true, - "recipients": ["alerts@example.com"], - "includeDiffs": true - } - }, - "targets": [ - { - "type": "scrape", - "urls": ["https://example.com/blog"] - } - ] - }' -``` + + + + + Create a crawl monitor to diff every page discovered by a crawl on each check: -```bash -curl -X POST "https://api.firecrawl.dev/v2/monitor" \ - -H "Authorization: Bearer $FIRECRAWL_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "Docs monitor", - "schedule": { - "cron": "7-59/15 * * * *", - "timezone": "UTC" - }, - "webhook": { - "url": "https://example.com/webhooks/firecrawl", - "events": ["monitor.page", "monitor.check.completed"] - }, - "targets": [ - { - "type": "crawl", - "url": "https://example.com/docs", - "crawlOptions": { - "limit": 100, - "maxDepth": 3 - } - } - ] - }' -``` + + + + + + +Every create call returns the new monitor with its normalized cron, computed `nextRunAt`, and `estimatedCreditsPerMonth`: + + ## Schedules Schedules can be provided as cron or as simple natural language text. -```json -{ - "schedule": { - "cron": "*/30 * * * *", - "timezone": "UTC" - } -} -``` - -```json -{ - "schedule": { - "text": "every 30 minutes", - "timezone": "UTC" - } -} -``` + + + + Supported natural language examples: @@ -116,32 +95,51 @@ Monitors support two target types: Target scrape options are passed through to the underlying scrape jobs. Monitor-triggered scrapes default `maxAge` to `0`, so each check performs a fresh scrape unless you explicitly set a different `maxAge`. -```json -{ - "type": "scrape", - "urls": ["https://example.com/pricing"], - "scrapeOptions": { - "formats": ["markdown"], - "maxAge": 0 - } -} -``` + For crawl targets, use `crawlOptions` for crawl behavior and `scrapeOptions` for each page scrape: -```json -{ - "type": "crawl", - "url": "https://example.com/docs", - "crawlOptions": { - "limit": 100, - "includePaths": ["/docs"] - }, - "scrapeOptions": { - "formats": ["markdown"] - } -} -``` + + +## Change tracking + +By default Firecrawl diffs each page's markdown and reports `same`, `changed`, `new`, `removed`, or `error`. When you want to detect changes in **specific structured fields** (price, headline, in-stock flag, the items in a list, etc.), enable JSON-mode change tracking by adding a `changeTracking` format with `modes: ["json"]` to the target's `scrapeOptions`. + +### Markdown mode (default) + +When `scrapeOptions.formats` is just `["markdown"]`, each changed page in the check response carries a unified text diff plus a [parseDiff](https://github.com/sergeyt/parse-diff)-style AST: + + + +### JSON mode + +Pass a `changeTracking` format with `modes: ["json"]` together with a JSON schema (or a `prompt`) describing the fields you care about. Firecrawl extracts that JSON on every check and emits a **per-field diff** keyed by the field path, plus a `snapshot.json` with the full current extraction so consumers don't need to re-fetch the underlying scrape. + + + + + + + +The diff payload looks like this — keys are JSON paths into the extraction, and each value is a `{previous, current}` pair: + + + + + Even if no tracked field changed but the surrounding markdown did, JSON-mode monitors still report `same` unless you also enable git-diff (see mixed mode below). The diff focuses purely on the fields in your schema. + + +### Mixed mode (JSON + git-diff) + +If you want both surfaces — the structured per-field diff **and** the raw markdown unified diff — pass both modes: + + + +The check response then contains both `diff.text` (markdown sidecar) and `diff.json` (per-field diff), along with the `snapshot.json` extraction: + + + +A mixed-mode page reports `changed` whenever **either** surface changed. ## Notifications @@ -152,70 +150,15 @@ When a monitor has a `webhook`, Firecrawl can send two monitor events: - `monitor.page`: Sent as each monitored scrape finishes in the scrape worker. - `monitor.check.completed`: Sent after the full check is reconciled. Includes check status and summary counts. Use `monitor.page` events or the monitor check API for page-level results. -```json -{ - "webhook": { - "url": "https://example.com/webhooks/firecrawl", - "headers": { - "Authorization": "Bearer your-secret" - }, - "metadata": { - "environment": "production" - }, - "events": ["monitor.page", "monitor.check.completed"] - } -} -``` + `monitor.page` payload: -```json -{ - "success": true, - "type": "monitor.page", - "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", - "webhookId": "f1e2d3c4-0000-0000-0000-000000000000", - "data": { - "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", - "checkId": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", - "url": "https://example.com/blog", - "status": "changed", - "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", - "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", - "error": null - }, - "metadata": { - "environment": "production" - } -} -``` + `monitor.check.completed` payload: -```json -{ - "success": true, - "type": "monitor.check.completed", - "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", - "webhookId": "f1e2d3c4-0001-0000-0000-000000000000", - "data": { - "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", - "checkId": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", - "status": "completed", - "summary": { - "totalPages": 2, - "same": 1, - "changed": 1, - "new": 0, - "removed": 0, - "error": 0 - } - }, - "metadata": { - "environment": "production" - } -} -``` + `success` is `true` when the check completed without page errors. It is `false` for failed or partial checks, and `error` contains the failure reason when available. @@ -223,63 +166,33 @@ When a monitor has a `webhook`, Firecrawl can send two monitor events: Email summaries are sent only when a check has changed, new, removed, or errored pages. -```json -{ - "notification": { - "email": { - "enabled": true, - "recipients": ["alerts@example.com"], - "includeDiffs": true - } - } -} -``` + If `recipients` is omitted, Firecrawl sends to team members who are eligible for system alert emails. ## Check results -Use `GET /v2/monitor/{monitorId}/checks` to list checks and `GET /v2/monitor/{monitorId}/checks/{checkId}` to inspect a check. - -```bash -curl "https://api.firecrawl.dev/v2/monitor/$MONITOR_ID/checks/$CHECK_ID?limit=25" \ - -H "Authorization: Bearer $FIRECRAWL_API_KEY" -``` - -The check detail response includes summary counts and a paginated `pages` array. Use the top-level `next` URL to fetch the next page of results, matching crawl pagination. Changed pages include inline diff data when available. - -```json -{ - "success": true, - "next": "https://api.firecrawl.dev/v2/monitor/019df960-06e7-7383-9d89-82c0113dc31a/checks/019df960-5f2a-75fb-a98b-bd2d32ca67d4?skip=25&limit=25", - "data": { - "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", - "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", - "status": "completed", - "summary": { - "totalPages": 1, - "same": 0, - "changed": 1, - "new": 0, - "removed": 0, - "error": 0 - }, - "pages": [ - { - "url": "https://example.com/blog", - "status": "changed", - "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", - "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", - "diff": { - "text": "...", - "json": {} - } - } - ], - "next": "https://api.firecrawl.dev/v2/monitor/019df960-06e7-7383-9d89-82c0113dc31a/checks/019df960-5f2a-75fb-a98b-bd2d32ca67d4?skip=25&limit=25" - } -} -``` +Use `GET /v2/monitor/{monitorId}/checks` to list checks and `GET /v2/monitor/{monitorId}/checks/{checkId}` to inspect a check. The SDKs auto-paginate by default. + + + + + + + +The check detail response includes summary counts and a paginated `pages` array. Use the top-level `next` URL to fetch the next page of results, matching crawl pagination. Each changed page includes inline `diff` data; pages from JSON-mode monitors also include a `snapshot` with the current extraction. + + + + + + + + + + + + ## API reference diff --git a/snippets/v2/monitor/check/get/curl.mdx b/snippets/v2/monitor/check/get/curl.mdx new file mode 100644 index 00000000..553437f0 --- /dev/null +++ b/snippets/v2/monitor/check/get/curl.mdx @@ -0,0 +1,4 @@ +```bash cURL +curl "https://api.firecrawl.dev/v2/monitor/$MONITOR_ID/checks/$CHECK_ID?limit=25" \ + -H "Authorization: Bearer $FIRECRAWL_API_KEY" +``` diff --git a/snippets/v2/monitor/check/get/js.mdx b/snippets/v2/monitor/check/get/js.mdx new file mode 100644 index 00000000..e358e2a4 --- /dev/null +++ b/snippets/v2/monitor/check/get/js.mdx @@ -0,0 +1,21 @@ +```js Node +import Firecrawl from "@mendable/firecrawl-js"; + +const firecrawl = new Firecrawl({ apiKey: "fc-YOUR-API-KEY" }); + +const check = await firecrawl.getMonitorCheck(monitorId, checkId, { + limit: 25, +}); + +for (const page of check.pages) { + console.log(page.url, page.status); + + if (page.diff?.text) { + console.log(page.diff.text); + } + + if (page.snapshot?.json) { + console.log(page.snapshot.json); + } +} +``` diff --git a/snippets/v2/monitor/check/get/python.mdx b/snippets/v2/monitor/check/get/python.mdx new file mode 100644 index 00000000..bb2c99f2 --- /dev/null +++ b/snippets/v2/monitor/check/get/python.mdx @@ -0,0 +1,16 @@ +```python Python +from firecrawl import Firecrawl + +firecrawl = Firecrawl(api_key="fc-YOUR-API-KEY") + +check = firecrawl.get_monitor_check(monitor_id, check_id, limit=25) + +for page in check.pages: + print(page.url, page.status) + + if page.diff and page.diff.text: + print(page.diff.text) + + if page.snapshot and page.snapshot.json: + print(page.snapshot.json) +``` diff --git a/snippets/v2/monitor/check/output/json.mdx b/snippets/v2/monitor/check/output/json.mdx new file mode 100644 index 00000000..5fcfedd4 --- /dev/null +++ b/snippets/v2/monitor/check/output/json.mdx @@ -0,0 +1,54 @@ +```json JSON-mode response +{ + "success": true, + "data": { + "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", + "status": "completed", + "summary": { + "totalPages": 1, + "same": 0, + "changed": 1, + "new": 0, + "removed": 0, + "error": 0 + }, + "pages": [ + { + "url": "https://example.com/pricing", + "status": "changed", + "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", + "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", + "diff": { + "json": { + "plans[0].price": { + "previous": "$19/mo", + "current": "$24/mo" + }, + "plans[1].features[2]": { + "previous": "10 GB storage", + "current": "25 GB storage" + } + } + }, + "snapshot": { + "json": { + "plans": [ + { + "name": "Starter", + "price": "$24/mo", + "features": ["Up to 3 users", "Basic analytics", "Email support"] + }, + { + "name": "Pro", + "price": "$49/mo", + "features": ["Unlimited users", "Advanced analytics", "25 GB storage"] + } + ] + } + } + } + ] + } +} +``` diff --git a/snippets/v2/monitor/check/output/markdown.mdx b/snippets/v2/monitor/check/output/markdown.mdx new file mode 100644 index 00000000..08e735a0 --- /dev/null +++ b/snippets/v2/monitor/check/output/markdown.mdx @@ -0,0 +1,40 @@ +```json Markdown-mode response +{ + "success": true, + "next": "https://api.firecrawl.dev/v2/monitor/019df960-06e7-7383-9d89-82c0113dc31a/checks/019df960-5f2a-75fb-a98b-bd2d32ca67d4?skip=25&limit=25", + "data": { + "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", + "status": "completed", + "summary": { + "totalPages": 1, + "same": 0, + "changed": 1, + "new": 0, + "removed": 0, + "error": 0 + }, + "pages": [ + { + "url": "https://example.com/blog", + "status": "changed", + "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", + "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", + "diff": { + "text": "--- previous\n+++ current\n@@ -1,3 +1,3 @@\n # Latest posts\n-Welcome to our weekly update.\n+Welcome to our weekly update — now with daily releases!\n", + "json": { + "files": [ + { + "from": "previous", + "to": "current", + "chunks": [] + } + ] + } + } + } + ], + "next": "https://api.firecrawl.dev/v2/monitor/019df960-06e7-7383-9d89-82c0113dc31a/checks/019df960-5f2a-75fb-a98b-bd2d32ca67d4?skip=25&limit=25" + } +} +``` diff --git a/snippets/v2/monitor/check/output/mixed.mdx b/snippets/v2/monitor/check/output/mixed.mdx new file mode 100644 index 00000000..f19537be --- /dev/null +++ b/snippets/v2/monitor/check/output/mixed.mdx @@ -0,0 +1,43 @@ +```json Mixed-mode response (JSON + git-diff) +{ + "success": true, + "data": { + "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", + "status": "completed", + "summary": { + "totalPages": 1, + "same": 0, + "changed": 1, + "new": 0, + "removed": 0, + "error": 0 + }, + "pages": [ + { + "url": "https://example.com/pricing", + "status": "changed", + "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", + "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", + "diff": { + "text": "--- previous\n+++ current\n@@ -1,3 +1,3 @@\n # Pricing\n-Starter — $19/mo\n+Starter — $24/mo\n", + "json": { + "plans[0].price": { + "previous": "$19/mo", + "current": "$24/mo" + } + } + }, + "snapshot": { + "json": { + "plans": [ + { "name": "Starter", "price": "$24/mo" }, + { "name": "Pro", "price": "$49/mo" } + ] + } + } + } + ] + } +} +``` diff --git a/snippets/v2/monitor/create/crawl/curl.mdx b/snippets/v2/monitor/create/crawl/curl.mdx new file mode 100644 index 00000000..42aacf4a --- /dev/null +++ b/snippets/v2/monitor/create/crawl/curl.mdx @@ -0,0 +1,26 @@ +```bash cURL +curl -s -X POST "https://api.firecrawl.dev/v2/monitor" \ + -H "Authorization: Bearer $FIRECRAWL_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Docs monitor", + "schedule": { + "cron": "7-59/15 * * * *", + "timezone": "UTC" + }, + "webhook": { + "url": "https://example.com/webhooks/firecrawl", + "events": ["monitor.page", "monitor.check.completed"] + }, + "targets": [ + { + "type": "crawl", + "url": "https://example.com/docs", + "crawlOptions": { + "limit": 100, + "maxDiscoveryDepth": 3 + } + } + ] + }' +``` diff --git a/snippets/v2/monitor/create/crawl/js.mdx b/snippets/v2/monitor/create/crawl/js.mdx new file mode 100644 index 00000000..a081f3b2 --- /dev/null +++ b/snippets/v2/monitor/create/crawl/js.mdx @@ -0,0 +1,26 @@ +```js Node +import Firecrawl from "@mendable/firecrawl-js"; + +const firecrawl = new Firecrawl({ apiKey: "fc-YOUR-API-KEY" }); + +const monitor = await firecrawl.createMonitor({ + name: "Docs monitor", + schedule: { cron: "7-59/15 * * * *", timezone: "UTC" }, + webhook: { + url: "https://example.com/webhooks/firecrawl", + events: ["monitor.page", "monitor.check.completed"], + }, + targets: [ + { + type: "crawl", + url: "https://example.com/docs", + crawlOptions: { + limit: 100, + maxDiscoveryDepth: 3, + }, + }, + ], +}); + +console.log(monitor.id); +``` diff --git a/snippets/v2/monitor/create/crawl/python.mdx b/snippets/v2/monitor/create/crawl/python.mdx new file mode 100644 index 00000000..01c300ba --- /dev/null +++ b/snippets/v2/monitor/create/crawl/python.mdx @@ -0,0 +1,26 @@ +```python Python +from firecrawl import Firecrawl + +firecrawl = Firecrawl(api_key="fc-YOUR-API-KEY") + +monitor = firecrawl.create_monitor( + name="Docs monitor", + schedule={"cron": "7-59/15 * * * *", "timezone": "UTC"}, + targets=[ + { + "type": "crawl", + "url": "https://example.com/docs", + "crawlOptions": { + "limit": 100, + "maxDiscoveryDepth": 3, + }, + } + ], + webhook={ + "url": "https://example.com/webhooks/firecrawl", + "events": ["monitor.page", "monitor.check.completed"], + }, +) + +print(monitor.id) +``` diff --git a/snippets/v2/monitor/create/json/curl.mdx b/snippets/v2/monitor/create/json/curl.mdx new file mode 100644 index 00000000..5c38125d --- /dev/null +++ b/snippets/v2/monitor/create/json/curl.mdx @@ -0,0 +1,53 @@ +```bash cURL +curl -s -X POST "https://api.firecrawl.dev/v2/monitor" \ + -H "Authorization: Bearer $FIRECRAWL_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Pricing monitor", + "schedule": { + "text": "hourly", + "timezone": "UTC" + }, + "notification": { + "email": { + "enabled": true, + "recipients": ["alerts@example.com"], + "includeDiffs": true + } + }, + "targets": [ + { + "type": "scrape", + "urls": ["https://example.com/pricing"], + "scrapeOptions": { + "formats": [ + { + "type": "changeTracking", + "modes": ["json"], + "prompt": "Extract pricing tiers and headline features for each plan.", + "schema": { + "type": "object", + "properties": { + "plans": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "price": { "type": "string" }, + "features": { + "type": "array", + "items": { "type": "string" } + } + } + } + } + } + } + } + ] + } + } + ] + }' +``` diff --git a/snippets/v2/monitor/create/json/js.mdx b/snippets/v2/monitor/create/json/js.mdx new file mode 100644 index 00000000..16c06b44 --- /dev/null +++ b/snippets/v2/monitor/create/json/js.mdx @@ -0,0 +1,46 @@ +```js Node +import Firecrawl from "@mendable/firecrawl-js"; +import { z } from "zod"; + +const firecrawl = new Firecrawl({ apiKey: "fc-YOUR-API-KEY" }); + +const pricingSchema = z.object({ + plans: z.array( + z.object({ + name: z.string(), + price: z.string(), + features: z.array(z.string()), + }), + ), +}); + +const monitor = await firecrawl.createMonitor({ + name: "Pricing monitor", + schedule: { text: "hourly", timezone: "UTC" }, + notification: { + email: { + enabled: true, + recipients: ["alerts@example.com"], + includeDiffs: true, + }, + }, + targets: [ + { + type: "scrape", + urls: ["https://example.com/pricing"], + scrapeOptions: { + formats: [ + { + type: "changeTracking", + modes: ["json"], + prompt: "Extract pricing tiers and headline features for each plan.", + schema: pricingSchema, + }, + ], + }, + }, + ], +}); + +console.log(monitor.id); +``` diff --git a/snippets/v2/monitor/create/json/python.mdx b/snippets/v2/monitor/create/json/python.mdx new file mode 100644 index 00000000..775ab9c0 --- /dev/null +++ b/snippets/v2/monitor/create/json/python.mdx @@ -0,0 +1,48 @@ +```python Python +from firecrawl import Firecrawl +from pydantic import BaseModel +from typing import List + +firecrawl = Firecrawl(api_key="fc-YOUR-API-KEY") + + +class Plan(BaseModel): + name: str + price: str + features: List[str] + + +class Pricing(BaseModel): + plans: List[Plan] + + +monitor = firecrawl.create_monitor( + name="Pricing monitor", + schedule={"text": "hourly", "timezone": "UTC"}, + targets=[ + { + "type": "scrape", + "urls": ["https://example.com/pricing"], + "scrapeOptions": { + "formats": [ + { + "type": "changeTracking", + "modes": ["json"], + "prompt": "Extract pricing tiers and headline features for each plan.", + "schema": Pricing.model_json_schema(), + } + ] + }, + } + ], + notification={ + "email": { + "enabled": True, + "recipients": ["alerts@example.com"], + "includeDiffs": True, + } + }, +) + +print(monitor.id) +``` diff --git a/snippets/v2/monitor/create/output.mdx b/snippets/v2/monitor/create/output.mdx new file mode 100644 index 00000000..9b7e0e7e --- /dev/null +++ b/snippets/v2/monitor/create/output.mdx @@ -0,0 +1,37 @@ +```json Response +{ + "success": true, + "data": { + "id": "019df960-06e7-7383-9d89-82c0113dc31a", + "name": "Blog monitor", + "status": "active", + "schedule": { + "cron": "*/30 * * * *", + "timezone": "UTC" + }, + "nextRunAt": "2026-05-17T16:00:00.000Z", + "lastRunAt": null, + "currentCheckId": null, + "targets": [ + { + "id": "019df960-09bb-7c11-8001-1f12f50ab1c2", + "type": "scrape", + "urls": ["https://example.com/blog"] + } + ], + "webhook": null, + "notification": { + "email": { + "enabled": true, + "recipients": ["alerts@example.com"], + "includeDiffs": true + } + }, + "retentionDays": 30, + "estimatedCreditsPerMonth": 1440, + "lastCheckSummary": null, + "createdAt": "2026-05-17T15:30:00.000Z", + "updatedAt": "2026-05-17T15:30:00.000Z" + } +} +``` diff --git a/snippets/v2/monitor/create/scrape/curl.mdx b/snippets/v2/monitor/create/scrape/curl.mdx new file mode 100644 index 00000000..d15db5be --- /dev/null +++ b/snippets/v2/monitor/create/scrape/curl.mdx @@ -0,0 +1,25 @@ +```bash cURL +curl -s -X POST "https://api.firecrawl.dev/v2/monitor" \ + -H "Authorization: Bearer $FIRECRAWL_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Blog monitor", + "schedule": { + "text": "every 30 minutes", + "timezone": "UTC" + }, + "notification": { + "email": { + "enabled": true, + "recipients": ["alerts@example.com"], + "includeDiffs": true + } + }, + "targets": [ + { + "type": "scrape", + "urls": ["https://example.com/blog"] + } + ] + }' +``` diff --git a/snippets/v2/monitor/create/scrape/js.mdx b/snippets/v2/monitor/create/scrape/js.mdx new file mode 100644 index 00000000..ebec0952 --- /dev/null +++ b/snippets/v2/monitor/create/scrape/js.mdx @@ -0,0 +1,25 @@ +```js Node +import Firecrawl from "@mendable/firecrawl-js"; + +const firecrawl = new Firecrawl({ apiKey: "fc-YOUR-API-KEY" }); + +const monitor = await firecrawl.createMonitor({ + name: "Blog monitor", + schedule: { text: "every 30 minutes", timezone: "UTC" }, + notification: { + email: { + enabled: true, + recipients: ["alerts@example.com"], + includeDiffs: true, + }, + }, + targets: [ + { + type: "scrape", + urls: ["https://example.com/blog"], + }, + ], +}); + +console.log(monitor.id); +``` diff --git a/snippets/v2/monitor/create/scrape/python.mdx b/snippets/v2/monitor/create/scrape/python.mdx new file mode 100644 index 00000000..e4ae3c6b --- /dev/null +++ b/snippets/v2/monitor/create/scrape/python.mdx @@ -0,0 +1,25 @@ +```python Python +from firecrawl import Firecrawl + +firecrawl = Firecrawl(api_key="fc-YOUR-API-KEY") + +monitor = firecrawl.create_monitor( + name="Blog monitor", + schedule={"text": "every 30 minutes", "timezone": "UTC"}, + targets=[ + { + "type": "scrape", + "urls": ["https://example.com/blog"], + } + ], + notification={ + "email": { + "enabled": True, + "recipients": ["alerts@example.com"], + "includeDiffs": True, + } + }, +) + +print(monitor.id) +``` diff --git a/snippets/v2/monitor/diff/json.mdx b/snippets/v2/monitor/diff/json.mdx new file mode 100644 index 00000000..9fa2cdeb --- /dev/null +++ b/snippets/v2/monitor/diff/json.mdx @@ -0,0 +1,32 @@ +```json JSON-mode diff +{ + "diff": { + "json": { + "plans[0].price": { + "previous": "$19/mo", + "current": "$24/mo" + }, + "plans[1].features[2]": { + "previous": "10 GB storage", + "current": "25 GB storage" + } + } + }, + "snapshot": { + "json": { + "plans": [ + { + "name": "Starter", + "price": "$24/mo", + "features": ["Up to 3 users", "Basic analytics", "Email support"] + }, + { + "name": "Pro", + "price": "$49/mo", + "features": ["Unlimited users", "Advanced analytics", "25 GB storage"] + } + ] + } + } +} +``` diff --git a/snippets/v2/monitor/diff/markdown.mdx b/snippets/v2/monitor/diff/markdown.mdx new file mode 100644 index 00000000..06e8ba87 --- /dev/null +++ b/snippets/v2/monitor/diff/markdown.mdx @@ -0,0 +1,21 @@ +```json Markdown-mode diff +{ + "diff": { + "text": "--- previous\n+++ current\n@@ -1,3 +1,3 @@\n # Pricing\n-Starter — $19/mo\n+Starter — $24/mo\n", + "json": { + "files": [ + { + "from": "previous", + "to": "current", + "chunks": [ + { + "content": "@@ -1,3 +1,3 @@", + "changes": [] + } + ] + } + ] + } + } +} +``` diff --git a/snippets/v2/monitor/diff/mixed.mdx b/snippets/v2/monitor/diff/mixed.mdx new file mode 100644 index 00000000..fde1a00c --- /dev/null +++ b/snippets/v2/monitor/diff/mixed.mdx @@ -0,0 +1,21 @@ +```json Mixed-mode diff (JSON + git-diff) +{ + "diff": { + "text": "--- previous\n+++ current\n@@ -1,3 +1,3 @@\n # Pricing\n-Starter — $19/mo\n+Starter — $24/mo\n", + "json": { + "plans[0].price": { + "previous": "$19/mo", + "current": "$24/mo" + } + } + }, + "snapshot": { + "json": { + "plans": [ + { "name": "Starter", "price": "$24/mo" }, + { "name": "Pro", "price": "$49/mo" } + ] + } + } +} +``` diff --git a/snippets/v2/monitor/notification/check-completed-payload.mdx b/snippets/v2/monitor/notification/check-completed-payload.mdx new file mode 100644 index 00000000..f45a3e36 --- /dev/null +++ b/snippets/v2/monitor/notification/check-completed-payload.mdx @@ -0,0 +1,24 @@ +```json monitor.check.completed +{ + "success": true, + "type": "monitor.check.completed", + "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "webhookId": "f1e2d3c4-0001-0000-0000-000000000000", + "data": { + "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", + "checkId": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "status": "completed", + "summary": { + "totalPages": 2, + "same": 1, + "changed": 1, + "new": 0, + "removed": 0, + "error": 0 + } + }, + "metadata": { + "environment": "production" + } +} +``` diff --git a/snippets/v2/monitor/notification/email.mdx b/snippets/v2/monitor/notification/email.mdx new file mode 100644 index 00000000..401d93e2 --- /dev/null +++ b/snippets/v2/monitor/notification/email.mdx @@ -0,0 +1,11 @@ +```json Email config +{ + "notification": { + "email": { + "enabled": true, + "recipients": ["alerts@example.com"], + "includeDiffs": true + } + } +} +``` diff --git a/snippets/v2/monitor/notification/page-payload.mdx b/snippets/v2/monitor/notification/page-payload.mdx new file mode 100644 index 00000000..716fab18 --- /dev/null +++ b/snippets/v2/monitor/notification/page-payload.mdx @@ -0,0 +1,20 @@ +```json monitor.page +{ + "success": true, + "type": "monitor.page", + "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "webhookId": "f1e2d3c4-0000-0000-0000-000000000000", + "data": { + "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", + "checkId": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", + "url": "https://example.com/blog", + "status": "changed", + "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", + "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", + "error": null + }, + "metadata": { + "environment": "production" + } +} +``` diff --git a/snippets/v2/monitor/notification/webhook.mdx b/snippets/v2/monitor/notification/webhook.mdx new file mode 100644 index 00000000..806f8bc2 --- /dev/null +++ b/snippets/v2/monitor/notification/webhook.mdx @@ -0,0 +1,14 @@ +```json Webhook config +{ + "webhook": { + "url": "https://example.com/webhooks/firecrawl", + "headers": { + "Authorization": "Bearer your-secret" + }, + "metadata": { + "environment": "production" + }, + "events": ["monitor.page", "monitor.check.completed"] + } +} +``` diff --git a/snippets/v2/monitor/schedule/cron.mdx b/snippets/v2/monitor/schedule/cron.mdx new file mode 100644 index 00000000..69e6cff6 --- /dev/null +++ b/snippets/v2/monitor/schedule/cron.mdx @@ -0,0 +1,8 @@ +```json Cron +{ + "schedule": { + "cron": "*/30 * * * *", + "timezone": "UTC" + } +} +``` diff --git a/snippets/v2/monitor/schedule/text.mdx b/snippets/v2/monitor/schedule/text.mdx new file mode 100644 index 00000000..3228f7ea --- /dev/null +++ b/snippets/v2/monitor/schedule/text.mdx @@ -0,0 +1,8 @@ +```json Text +{ + "schedule": { + "text": "every 30 minutes", + "timezone": "UTC" + } +} +``` diff --git a/snippets/v2/monitor/target/crawl.mdx b/snippets/v2/monitor/target/crawl.mdx new file mode 100644 index 00000000..d372a5d2 --- /dev/null +++ b/snippets/v2/monitor/target/crawl.mdx @@ -0,0 +1,13 @@ +```json Crawl target +{ + "type": "crawl", + "url": "https://example.com/docs", + "crawlOptions": { + "limit": 100, + "includePaths": ["/docs"] + }, + "scrapeOptions": { + "formats": ["markdown"] + } +} +``` diff --git a/snippets/v2/monitor/target/json-mixed.mdx b/snippets/v2/monitor/target/json-mixed.mdx new file mode 100644 index 00000000..4358b8fe --- /dev/null +++ b/snippets/v2/monitor/target/json-mixed.mdx @@ -0,0 +1,30 @@ +```json Mixed target (JSON + git-diff) +{ + "type": "scrape", + "urls": ["https://example.com/pricing"], + "scrapeOptions": { + "formats": [ + { + "type": "changeTracking", + "modes": ["json", "git-diff"], + "prompt": "Extract pricing tiers and headline features for each plan.", + "schema": { + "type": "object", + "properties": { + "plans": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "price": { "type": "string" } + } + } + } + } + } + } + ] + } +} +``` diff --git a/snippets/v2/monitor/target/json.mdx b/snippets/v2/monitor/target/json.mdx new file mode 100644 index 00000000..a30501d3 --- /dev/null +++ b/snippets/v2/monitor/target/json.mdx @@ -0,0 +1,34 @@ +```json JSON-mode target +{ + "type": "scrape", + "urls": ["https://example.com/pricing"], + "scrapeOptions": { + "formats": [ + { + "type": "changeTracking", + "modes": ["json"], + "prompt": "Extract pricing tiers and headline features for each plan.", + "schema": { + "type": "object", + "properties": { + "plans": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "price": { "type": "string" }, + "features": { + "type": "array", + "items": { "type": "string" } + } + } + } + } + } + } + } + ] + } +} +``` diff --git a/snippets/v2/monitor/target/scrape.mdx b/snippets/v2/monitor/target/scrape.mdx new file mode 100644 index 00000000..e4544f47 --- /dev/null +++ b/snippets/v2/monitor/target/scrape.mdx @@ -0,0 +1,10 @@ +```json Scrape target +{ + "type": "scrape", + "urls": ["https://example.com/pricing"], + "scrapeOptions": { + "formats": ["markdown"], + "maxAge": 0 + } +} +``` diff --git a/webhooks/events.mdx b/webhooks/events.mdx index 8f024fc2..ca22a472 100644 --- a/webhooks/events.mdx +++ b/webhooks/events.mdx @@ -6,6 +6,9 @@ og:description: "Webhook event reference" icon: "bolt" --- +import MonitorPagePayload from "/snippets/v2/monitor/notification/page-payload.mdx"; +import MonitorCheckCompletedPayload from "/snippets/v2/monitor/notification/check-completed-payload.mdx"; + Firecrawl sends webhook events at each stage of a job's lifecycle, so you can track progress, capture results, and handle failures in real time without polling. ## Quick Reference @@ -180,51 +183,13 @@ Sent when all URLs in the batch have been processed. Sent as each monitored page scrape finishes. This event is emitted from the scrape worker path, so it arrives before the full monitor check is reconciled. -```json -{ - "success": true, - "type": "monitor.page", - "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", - "webhookId": "f1e2d3c4-0000-0000-0000-000000000000", - "data": { - "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", - "checkId": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", - "url": "https://example.com/blog", - "status": "changed", - "previousScrapeId": "019df94f-82c3-7e41-81f0-00c72b2d9c52", - "currentScrapeId": "019df960-73ee-7ac2-97a9-fb0e442c21f1", - "error": null - }, - "metadata": {} -} -``` + ### `monitor.check.completed` Sent when a monitor check finishes. The `data` object contains check status and summary counts. Page-level results are only sent through `monitor.page` events or returned from the monitor check API. -```json -{ - "success": true, - "type": "monitor.check.completed", - "id": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", - "webhookId": "f1e2d3c4-0001-0000-0000-000000000000", - "data": { - "monitorId": "019df960-06e7-7383-9d89-82c0113dc31a", - "checkId": "019df960-5f2a-75fb-a98b-bd2d32ca67d4", - "status": "completed", - "summary": { - "totalPages": 2, - "same": 1, - "changed": 1, - "new": 0, - "removed": 0, - "error": 0 - } - }, - "metadata": {} -} -``` + `success` is `true` when the check completed without page errors. For partial or failed checks, `success` is `false` and `error` may contain a message.