Docs / Strand / advanced/approval-links
External Approval Links
Allow reviewers to approve or reject workflow steps directly from email, Slack, or any external channel, with no login required.
Overview
When a workflow hits an approval node, Strand can generate a secure, signed URL that allows the recipient to take action without logging into the application. This is useful when approvers are not regular Strand users, or when you want to enable quick action from mobile devices or notification channels.
How It Works
- Workflow execution pauses at an approval node
- Strand generates a cryptographically signed token
- A URL is created:
https://app.tendrl.com/strand/approve/{token} - The URL is delivered via the configured notification connector (Slack, email, etc.)
- The recipient clicks the link and sees a standalone approval page
- They can approve or reject with an optional comment
- The workflow resumes (on approve) or stops (on reject)
Setup
Option A: Built-In Notification
Configure notify_connector_id directly on the approval node:
Slack example:
{
"message": "New expense report: ${{ payload.amount }} from {{ payload.employee }}",
"instructions": "Approve if amount is within department budget.",
"notify_connector_id": "slack-connector-id",
"notify_channel": "#expense-approvals",
"token_expiry_hours": 72
}
Email example:
{
"message": "New expense report: ${{ payload.amount }} from {{ payload.employee }}",
"instructions": "Approve if amount is within department budget.",
"notify_connector_id": "email-connector-id",
"notify_recipients": "{{ payload.manager_email }}",
"token_expiry_hours": 72
}
Strand will automatically:
- Generate a signed approval link
- Send the approval message plus link to the specified channel or recipients
- Format the notification appropriately for the connector type (Slack message, HTML email, or text message)
Option B: Connector Before Approval
For full control over the notification format, place a connector node before the approval node in your workflow graph. You can build the approval URL yourself using the token available in the notification metadata, or simply send a message saying "check Strand for a pending approval."
Security
External approval links are secured with multiple layers:
- Cryptographic signatures: Each token is signed so that any tampering invalidates it
- Automatic expiry: Tokens expire after the configured
token_expiry_hours(default: 7 days, max: 30 days) - Single-use enforcement: Once approved or rejected, the link cannot be reused
- Rate limiting: Public approval endpoints are rate-limited to prevent abuse
- Uniform error responses: All failure modes return the same generic error to prevent information leakage
URL Configuration
Approval links point to your Strand application's public URL. This is configured at the platform level by your administrator. No user-side setup is needed.
API Reference
Get Approval Details
GET /api/approval-actions/{token}
Returns approval context for display on the approval page.
Response:
{
"status": "waiting_approval",
"approval_message": "New expense report: $500 from Jane",
"approval_instructions": "Approve if within budget.",
"workflow_name": "Expense Approval",
"node_id": "approval_1",
"run_status": "waiting_approval",
"already_actioned": false,
"actioned_at": null
}
Approve via Token
POST /api/approval-actions/{token}/approve
Body:
{
"comment": "Approved; within budget."
}
Response:
{
"message": "Approved successfully. Workflow is resuming."
}
Reject via Token
POST /api/approval-actions/{token}/reject
Body:
{
"comment": "Over budget; please revise."
}
Response:
{
"message": "Rejected successfully.",
"run_status": "completed_with_errors"
}
Connector Payload
When using notify_connector_id, the connector receives a payload like:
{
"payload": {
"approval_message": "New expense report: $500 from Jane",
"approval_instructions": "Approve if within budget.",
"approval_url": "https://app.tendrl.com/strand/approve/eyJzIjoi...",
"workflow_name": "Expense Approval",
"run_id": "run_abc123",
"step_run_id": "step_xyz789"
},
"meta": {
"source": "approval_notification"
}
}
You can configure the connector's message template to format this however you like (e.g., a Slack message with action buttons linking to the approval URL).
Best Practices
- Set
token_expiry_hoursappropriate to your use case (shorter for time-sensitive approvals) - Include enough context in the
messageso the reviewer can decide without opening the app - Use
instructionsto document the review criteria - Test the approval flow end-to-end in development before deploying
Related
- Approval Node - Approval node reference
- Connectors - Setting up notification connectors
- Jinja2 Templating - Dynamic content in approval messages
Tendrl