Skip to content

Workflow Mode

Integrate Visiono into external workflows with secure signed URLs.

Overview

Workflow Mode allows external systems to embed Visiono photo collection seamlessly into their processes. Users are redirected from external systems (like Zendesk tickets or CRM records) to Visiono, complete photo uploads, and are automatically redirected back with verified data.

Key Features

FeatureDescription
Signed URLsHMAC-SHA256 signature prevents tampering
Auto-redirectReturn to external system after completion
Hidden FieldsPre-filled data not visible to users
Per-submission TrackingExternal source and reference stored
Signature VerificationVerify redirect data wasn't modified

How It Works

mermaid
sequenceDiagram
    participant E as External System
    participant V as Visiono
    participant U as User

    E->>E: Generate signed URL
    E->>U: Send link to user
    U->>V: Open signed URL
    V->>V: Validate signature
    V->>U: Show photo upload
    U->>V: Upload photos
    V->>E: Redirect with signature
    E->>E: Validate return signature

Configuration

1. Enable Workflow Mode

  1. Navigate to your Permanent Link
  2. Click Edit
  3. Expand Workflow Mode section
  4. Enable Workflow Mode toggle
  5. Configure redirect URL
  6. Copy the Workflow Secret

2. Configure Redirect URL

Set the URL users return to after completing uploads:

https://your-system.com/done?id={submission_id}&ref={reference}&sig={signature}

Available Placeholders

PlaceholderDescriptionExample
{submission_id}Visiono submission IDpls_123
{unique_value}Unique field value passedABC123
{reference}External reference passedticket-456
{photo_count}Number of photos uploaded5
{timestamp}Unix timestamp1705500000
{signature}HMAC signature for verificationabc123...

Generating Signed URLs

URL Parameters

ParamLong NameDescriptionRequired
uvunique_valueUnique identifierYes
srcsourceSource systemYes
refreferenceExternal referenceYes
tstimestampUnix timestampYes
sigsignatureHMAC-SHA256 signatureYes

Signature Algorithm

payload = "{unique_value}|{source}|{reference}|{timestamp}"
signature = HMAC-SHA256(payload, workflow_secret)

PHP Example

php
<?php

$workflowSecret = 'your-workflow-secret';
$slug = 'damage-report';

// Data
$uniqueValue = 'ABC123';
$source = 'zendesk';
$reference = 'ticket-456';
$timestamp = time();

// Generate signature
$payload = "{$uniqueValue}|{$source}|{$reference}|{$timestamp}";
$signature = hash_hmac('sha256', $payload, $workflowSecret);

// Build URL
$baseUrl = "https://visiono.io/s/{$slug}";
$params = http_build_query([
    'uv' => $uniqueValue,
    'src' => $source,
    'ref' => $reference,
    'ts' => $timestamp,
    'sig' => $signature,
]);

$url = "{$baseUrl}?{$params}";
// https://visiono.io/s/damage-report?uv=ABC123&src=zendesk&ref=ticket-456&ts=1705500000&sig=abc123...

JavaScript Example

javascript
const crypto = require('crypto');

const workflowSecret = 'your-workflow-secret';
const slug = 'damage-report';

// Data
const uniqueValue = 'ABC123';
const source = 'zendesk';
const reference = 'ticket-456';
const timestamp = Math.floor(Date.now() / 1000);

// Generate signature
const payload = `${uniqueValue}|${source}|${reference}|${timestamp}`;
const signature = crypto
  .createHmac('sha256', workflowSecret)
  .update(payload)
  .digest('hex');

// Build URL
const params = new URLSearchParams({
  uv: uniqueValue,
  src: source,
  ref: reference,
  ts: timestamp,
  sig: signature,
});

const url = `https://visiono.io/s/${slug}?${params}`;

Python Example

python
import hmac
import hashlib
import time
from urllib.parse import urlencode

workflow_secret = 'your-workflow-secret'
slug = 'damage-report'

# Data
unique_value = 'ABC123'
source = 'zendesk'
reference = 'ticket-456'
timestamp = int(time.time())

# Generate signature
payload = f"{unique_value}|{source}|{reference}|{timestamp}"
signature = hmac.new(
    workflow_secret.encode(),
    payload.encode(),
    hashlib.sha256
).hexdigest()

# Build URL
params = urlencode({
    'uv': unique_value,
    'src': source,
    'ref': reference,
    'ts': timestamp,
    'sig': signature,
})

url = f"https://visiono.io/s/{slug}?{params}"

Validating Redirect Signature

When users are redirected back, verify the signature to ensure data integrity.

Return URL Example

https://your-system.com/done?sub=pls_123&ref=ticket-456&cnt=5&ts=1705500100&sig=def456...

Signature Verification

The return signature is computed as:

payload = "{submission_id}|{unique_value}|{reference}|{photo_count}|{timestamp}"
expected_signature = HMAC-SHA256(payload, workflow_secret)

PHP Verification

php
<?php

$workflowSecret = 'your-workflow-secret';

// Get parameters from redirect
$submissionId = $_GET['sub'];
$uniqueValue = $_GET['uv'];
$reference = $_GET['ref'];
$photoCount = $_GET['cnt'];
$timestamp = $_GET['ts'];
$receivedSignature = $_GET['sig'];

// Verify signature
$payload = "{$submissionId}|{$uniqueValue}|{$reference}|{$photoCount}|{$timestamp}";
$expectedSignature = hash_hmac('sha256', $payload, $workflowSecret);

if (hash_equals($expectedSignature, $receivedSignature)) {
    // Signature valid - process the submission
    echo "Photos submitted successfully!";
} else {
    // Signature invalid - reject
    http_response_code(400);
    echo "Invalid signature";
}

Signed URLs expire after 1 hour by default. This prevents:

  • Old links from being reused
  • Replay attacks
  • Stale data

If a user opens an expired link, they see an "Link Expired" error page.

Security Best Practices

Protect Your Secret

  • Store the workflow secret securely
  • Never expose it in client-side code
  • Rotate secrets periodically
  • Use environment variables

Validate Timestamps

php
$maxAge = 3600; // 1 hour
if (time() - $timestamp > $maxAge) {
    // Link expired
    return false;
}

Use HTTPS

Always use HTTPS for:

  • Your redirect URLs
  • Any API calls
  • Webhook endpoints

Error Pages

Users may see error pages for:

ErrorCauseSolution
Missing ParametersURL lacks required paramsCheck URL generation
Invalid SignatureSignature doesn't matchVerify secret and algorithm
Link ExpiredTimestamp > 1 hour oldGenerate new link

Webhook Integration

Workflow Mode works with webhooks. The submission.created event includes:

json
{
  "event": "submission.created",
  "data": {
    "photo_request": {
      "id": "pr_123",
      "custom_slug": "damage-report"
    },
    "permanent_link_submission": {
      "id": "pls_456",
      "unique_field_value": "ABC123",
      "external_source": "zendesk",
      "external_reference": "ticket-456"
    }
  }
}

Use Cases

Zendesk Ticket Photos

  1. Agent sends customer signed link
  2. Customer uploads damage photos
  3. Customer redirected back to ticket
  4. Webhook updates ticket with photos

Insurance Claims

  1. Claim system generates signed link
  2. Policyholder documents damage
  3. Redirected to claim confirmation
  4. Photos automatically linked to claim

Field Service

  1. Technician receives work order
  2. Opens signed link from mobile
  3. Documents completed work
  4. Redirected to job completion page

Troubleshooting

Signature Mismatch

  1. Verify secret matches exactly
  2. Check parameter encoding
  3. Confirm timestamp format
  4. Ensure payload order is correct

Redirect Not Working

  1. Verify redirect URL is configured
  2. Check placeholder syntax
  3. Confirm URL is properly encoded
  1. Check server time synchronization
  2. Verify timestamp is Unix format
  3. Ensure timestamp is current

Professional Photo Documentation Platform