Scheduled Runs let you automate your notebook execution so it runs without manual intervention.
Free vs Paid Plan
- Free Plan – Schedule each doc to run up to once per week.
- Paid Plan – Unlimited scheduling options:
- Daily, Weekly, or Monthly schedules.
- Trigger schedules via webhooks from another service — or even another Livedocs notebook.
Creating a Scheduled Run
When creating a schedule, you can:
- Set the frequency (daily, weekly, monthly).
- Optionally enable webhook triggers.
- Choose notification methods for when the run completes.
Webhook Trigger Example
curl --request POST \
--url https://api.livedocs.com/v1/webhooks/<some-unique-id> \
--header 'Authorization: Bearer YOUR_WORKSPACE_API_KEY'
Exact code samples are shown in Livedocs when you create the schedule.
Notifications
You can be notified when a scheduled run completes via:
- Slack – Connect your Slack workspace via OAuth in Workspace Settings.
- Email – Enter recipient emails directly in the schedule creation modal.
- Webhook – Provide a URL for Livedocs to POST run details to.
Webhook Notification Details
- Livedocs sends a POST request with parameters like:
doc_id
– The ID of the document.- Other metadata about the run.
- The
doc_id
can be found as the unique characters in the document’s URL.
Tip: Combining webhook triggers and notifications allows you to chain notebook executions across systems.
Secure Your Webhook Endpoint
Every webhook notification is signed so you can make sure it came from Livedocs. We recommend verifying each request before processing it.
Step 1. Read the Signature Header
Callbacks include a Livedocs-Signature
header shaped like:
Livedocs-Signature: t=1696350000,v1=3d8c...
t
– Unix timestamp (seconds) when we sent the payload.v1
– HMAC-SHA256 signature created with your workspace API key.
Reject requests without this header.
def parse_signature(header: str) -> tuple[int, list[str]]:
parts = [item.split("=", 1) for item in header.split(",") if "=" in item]
values = {key: value for key, value in parts}
timestamp = int(values["t"]) # raises if missing
signatures = [v for key, v in parts if key == "v1"]
if not signatures:
raise ValueError("Missing v1 signature")
return timestamp, signatures
Step 2. Rebuild the Signed Payload
Use the exact raw request body (before JSON parsing) and concatenate it with the timestamp:
signed_payload = `${timestamp}.${raw_body}`
Step 3. Compute and Compare the Signature
expected = hmac.new(
key=WORKSPACE_API_KEY.encode("utf-8"),
msg=signed_payload.encode("utf-8"),
digestmod=hashlib.sha256,
).hexdigest()
valid = any(hmac.compare_digest(expected, candidate) for candidate in signatures)
if not valid:
raise ValueError("Invalid signature")
Use a constant-time comparison (as shown) and check that the timestamp is within the tolerence of your server clock (abs(now - timestamp) <= 300
). Drop callbacks that fall outside this window to defend against replay attacks.