Skip to main content

Documentation Index

Fetch the complete documentation index at: https://platform.docs.zenoo.com/llms.txt

Use this file to discover all available pages before exploring further.

Sharables

Sharables are a powerful feature that allows you to create secure, time-limited tokens for sharing executable workflows, functions, or data payloads. They enable asynchronous execution patterns, external integrations, and data polling mechanisms.

Overview

There are two types of sharables:
  • Payload Sharables - Store data that can be accessed across different executions
  • Execution Sharables - Create links that can be shared to start workflows or functions with specific data
The sharables and their data are stored in AWS DynamoDB table sharable. Related events are stored in sharable-events Kafka topic. Sharable events can be processed for real-time metrics and analytics purposes. There is also sharable-requests Kafka topic used internally by hub to process sharable requests and commands.

Table of Contents

Getting Started

Execution Sharables

Payload Sharables

Lifecycle Management

Events and Monitoring

Integration Patterns

Operations

Token Management

Token Generation

  • Default: 6 random alphanumeric characters (e.g., aB3xY9)
  • Custom: You can specify your own token
  • Collision Handling: If a token already exists, the operation will fail

Security Considerations

  • Tokens are bearer tokens - anyone with the token has full access
  • Use appropriate expiration times to limit exposure
  • Consider the security implications when sharing URLs containing tokens
  • Store tokens securely and avoid logging them in plain text

Default Expiration

  • Default expiration: 24 hours if not specified
  • Custom expiration: Use expireIn with ISO-8601 duration or simple format
  • Automatic cleanup: DynamoDB TTL automatically removes expired records

Execution Sharables

Execution sharables create links that can be shared to start workflows or functions with predefined or dynamic input data.

DSL Syntax

Basic Function Execution

// Simple function sharable
link << sharable {
    function 'process-payment'
}

// Function with predefined input and context
payment_link << sharable {
    function 'process-payment' {
        input amount: 100, currency: "USD"
        context merchant_id: "12345"
    }
    expireIn "PT1H"  // 1 hour
}

Workflow Execution

// Workflow sharable with custom URL
approval_link << sharable {
    url "https://approval.company.com/workflow/${token}"
    workflow 'approval-process' {
        input document_id: input.document_id
        context user_id: execution.context.user_id
    }
    reusable()  // Can be executed multiple times
}

Advanced Options

// Latest version resolution
sharable {
    function 'data-processor'
    latest()     // Resolve to latest version at execution time
    reusable()   // Allow multiple executions
    namespace result  // Store execution result asynchronously
    expireIn "P1D"    // 1 day (ISO-8601 format)
}

DSL Options Reference

OptionDescriptionExample
function(name)Execute a functionfunction 'my-function'
workflow(name)Execute a workflowworkflow 'my-workflow'
inputPredefined input datainput userId: "123"
contextPredefined context datacontext region: "us-east"
reusable()Allow multiple executionsreusable()
latest()Resolve version at execution timelatest()
namespaceStore result asynchronouslynamespace result
expireInCustom expiration timeexpireIn "30m"
url(template)Custom URL templateurl "https://example.com/${token}"

Use Cases

1. Email Notifications with Actions

function("send-approval-email") { input ->
    // Create approval and rejection links
    approve_link << sharable {
        url "https://company.com/approve/${token}"
        function 'approve-request' {
            input request_id: input.request_id
            context approver: input.approver_email
        }
        expireIn "P7D"  // 7 days
    }

    reject_link << sharable {
        url "https://company.com/reject/${token}"
        function 'reject-request' {
            input request_id: input.request_id
            context approver: input.approver_email
        }
        expireIn "P7D"
    }

    send_email([
        to: input.approver_email,
        subject: "Approval Required",
        body: """
        Please review the request:

        Approve: ${approve_link}
        Reject: ${reject_link}
        """
    ])
}

2. External System Integration

function("webhook-handler") { input ->
    // Create callback for external system
    callback_token << sharable {
        function 'process-webhook-response' {
            input webhook_id: input.webhook_id
        }
        expireIn "PT2H"  // 2 hours
    }

    // Send to external system
    http.post("https://external-system.com/api/process") {
        body([
            data: input.payload,
            callback_url: "https://our-hub.com/api/gateway/sharable/${callback_token}"
        ])
    }

    [
        status: "processing",
        callback_token: callback_token
    ]
}

3. Multi-step Workflows

workflow("document-approval") {
    route("Submit") {
        uri '/submit'

        // Create links for each approval step
        step1_link << sharable {
            workflow 'approval-step' {
                input step: 1, document_id: input.document_id
                context previous_step: execution.uuid
            }
            expireIn "P14D"  // 14 days
        }

        export step1_url: "https://portal.com/approve/${step1_link}"

        route("NextStep")
    }

    route("NextStep") {
        uri '/next-step'
        terminal()
    }
}

4. Reusable Service Endpoints

function("create-service-endpoint") { input ->
    service_token << sharable {
        reusable()  // Allow multiple calls
        function 'service-handler' {
            context service_config: input.config
        }
        expireIn "P30D"  // 30 days
    }

    [
        endpoint_url: "https://api.company.com/service/${service_token}",
        token: service_token,
        expires_at: now().plusDays(30)
    ]
}

API Endpoints

Execute Sharable

POST /api/gateway/sharable/{token}
Request Body: JSON payload (optional) - becomes execution input Response Codes:
  • 201 Created - Execution started successfully
  • 400 Bad Request - Token doesn’t exist
  • 410 Gone - Token has expired
  • 504 Gateway Timeout - Operation timeout
Example:
curl -X POST https://hub.example.com/api/gateway/sharable/aB3xY9 \
  -H "Content-Type: application/json" \
  -d '{"user_id": "12345", "action": "approve"}'
Response:
{
    "uuid": "89828e1e-c834-42a2-86f1-893209f63ab5",
    "requestURI": "/api/gateway/request/89828e1e-c834-42a2-86f1-893209f63ab5",
    "executionURI": "/api/gateway/execution/89828e1e-c834-42a2-86f1-893209f63ab5"
}

Payload Sharables

Payload sharables store data that can be accessed across different executions, enabling polling and data sharing patterns.

DSL Syntax

// Basic payload sharable with auto-generated token
token << sharable {
    payload application: [
        id: "12345",
        status: "pending"
    ]
}

// Custom token with expiration
sharable("custom-token-123") {
    payload result: calculation_result
    expireIn "PT30M"  // 30 minutes
}

// Simple expiration format
sharable {
    payload data: response_data
    expireIn "5s"     // 5 seconds
}

Use Cases

1. Polling Pattern

External systems can poll for data availability:
// Component generates data and provides polling token
exposed("generate-report") { input ->
    // Start async report generation
    report_data << async_report_generation(input)

    // Create polling token
    poll_token << sharable {
        payload result: report_data
        expireIn "1h"
    }

    [
        status: "processing",
        poll_url: "https://api.example.com/poll/${poll_token}",
        poll_token: poll_token
    ]
}

2. Data Caching

Store computed results for reuse:
function("expensive-calculation") { input ->
    cache_key << sharable {
        payload result: expensive_computation(input)
        expireIn "24h"
    }

    // Return both result and cache reference
    [
        result: expensive_computation(input),
        cache_token: cache_key
    ]
}

API Endpoints

Retrieve Payload Data

GET /api/gateway/sharable-payload/{token}
Response Codes:
  • 200 OK - Payload data returned as JSON
  • 404 Not Found - Token doesn’t exist or has expired
  • 504 Gateway Timeout - Operation timeout
Example:
curl -X GET https://hub.example.com/api/gateway/sharable-payload/aB3xY9
Response:
{
    "application": {
        "id": "12345",
        "status": "completed"
    }
}

Expiration and Lifecycle

Expiration Behavior

  • Payload Sharables: Expire based on expireIn setting or default (24h)
  • Execution Sharables:
    • Reusable: Only expire based on time settings
    • Single-use: Expire after execution terminates OR time limit

Expiration Formats

// ISO-8601 duration format
expireIn "PT30M"    // 30 minutes
expireIn "PT2H"     // 2 hours
expireIn "P1D"      // 1 day
expireIn "P7D"      // 7 days

// Simple format
expireIn "30s"      // 30 seconds
expireIn "5m"       // 5 minutes
expireIn "2h"       // 2 hours
expireIn "1d"       // 1 day

Manual Expiration

// Expire a specific token
sharable('token-to-expire') {
    expired()
}

// Expire the current execution's sharable token
sharable(execution.sharable) {
    expired()
}

Event System

Payload Sharable Events

CreatedPayloadSharable

Produced when payload sharable is created:
  • token - identifier of created sharable

ExpiredSharable

Produced when payload sharable expires:
  • token - identifier of expired sharable
  • executable - associated function/workflow (if any)
  • requestContext - execution context

Execution Sharable Events

CreatedExecuteSharable

Produced when execution sharable is created:
  • token - identifier of created sharable
  • reusable - whether sharable can be executed repeatedly
  • executable - function/workflow to execute
  • requestContext - execution context

ExecutedSharable

Produced when execution sharable is invoked:
  • token - identifier of executed sharable
  • executable - function/workflow executed
  • requestContext - execution context

TerminatedSharable

Produced when workflow started by execution sharable terminates:
  • token - identifier of terminated sharable
  • executable - function/workflow terminated
  • requestContext - execution context

Common Events

DeletedSharable

Produced when a sharable is deleted:
  • token - identifier of deleted sharable

Error Events

All error events include:
  • token - identifier of sharable that failed
  • message - error message

CreatingPayloadSharableError

Produced when creating payload sharable fails

CreatingExecuteSharableError

Produced when creating execute sharable fails

DeletingSharableError

Produced when deleting sharable fails

ExecutingSharableError

Produced when executing sharable fails:
  • errorType - GENERAL, SHARABLE_EXPIRED, SHARABLE_NOT_FOUND, COMPONENT_NOT_FOUND, NO_EXECUTE_REQUEST
  • requestContext - execution context

ExpiringSharableError

Produced when expiring sharable fails

Integration Patterns

1. Async Processing with Polling

// Service endpoint for long-running tasks
exposed("start-processing") { input ->
    // Start async processing
    processing_job << async_processor(input.data)

    // Create status polling token
    status_token << sharable {
        payload status: processing_job.status,
                progress: processing_job.progress,
                result: processing_job.result
        expireIn "24h"
    }

    [
        job_id: processing_job.id,
        status_url: "https://api.company.com/status/${status_token}",
        estimated_completion: "PT2H"
    ]
}

2. External Callback Integration

function("external-api-call") { input ->
    callback_token << sharable {
        function 'handle-external-response' {
            input original_request: input
        }
        expireIn "PT1H"
    }

    http.post("https://external-api.com/process") {
        body([
            data: input.payload,
            webhook_url: "https://our-hub.com/api/gateway/sharable/${callback_token}"
        ])
    }

    // Wait for callback or timeout
    await(callback_response, "55m") {
        timeout {
            log "External API call timed out"
            [status: "timeout"]
        }
        success {
            callback_response
        }
    }
}

3. User Action Workflows

workflow("user-approval-flow") {
    route("RequestApproval") {
        uri '/request'

        // Create user action links
        approve_token << sharable {
            workflow 'approval-granted' {
                input request_id: input.request_id
                context decision: "approved"
            }
        }

        reject_token << sharable {
            workflow 'approval-denied' {
                input request_id: input.request_id
                context decision: "rejected"
            }
        }

        // Send notification
        send_notification([
            user: input.approver,
            message: "Approval required",
            approve_url: "https://portal.com/approve/${approve_token}",
            reject_url: "https://portal.com/reject/${reject_token}"
        ])

        route("AwaitDecision")
    }

    route("AwaitDecision") {
        uri '/await'

        await(decision_result, "P7D") {
            timeout {
                export status: "expired"
                route("Timeout")
            }
            success {
                export status: "decided", result: decision_result
                route("Complete")
            }
        }
    }
}

4. Multi-tenant Service Access

function("create-tenant-access") { input ->
    tenant_token << sharable {
        reusable()  // Allow multiple API calls
        function 'tenant-api-handler' {
            context tenant_id: input.tenant_id,
                    permissions: input.permissions
        }
        expireIn "P90D"  // 90 days
    }

    [
        tenant_id: input.tenant_id,
        api_token: tenant_token,
        api_url: "https://api.company.com/tenant/${tenant_token}",
        expires_at: now().plusDays(90)
    ]
}

5. Mobile Hand-off with Terminal Route Result

Pass execution control to a mobile device or external system, then receive the result back into the main execution flow:
workflow("mobile-handoff-flow") {
    payload ->
        // Create a sharable workflow for mobile execution
        sharable(payload) {
            workflow 'mobile-workflow'
            namespace handoff  // Result will be stored in this namespace
            expireIn "PT1H"    // 1 hour for user to complete on mobile
        }

        // Wait for mobile workflow to complete and return result
        await(handoff, "1h") {
            onTimeout {
                // Handle timeout if user doesn't complete mobile workflow
                log.error("Mobile verification timeout")
            }
        }

        // After await completes, mobile workflow result is available in handoff namespace
        route('result') {
            uri '/result'
            export handoff  // Export the result received from mobile workflow
            terminal()
        }
}

// Mobile workflow that runs independently on mobile device
workflow("mobile-workflow") {
    payload ->
        route('verify-identity') {
            uri '/verify'
            // User performs biometric verification or other mobile actions
        }

        route('capture-data') {
            uri '/capture'
            // Capture photos, signatures, etc.
        }

        route('complete') {
            uri '/complete'
            // Terminal route with result data that gets passed back to main execution
            terminal([
                verified: true,
                biometric_score: input.biometric_score,
                captured_images: input.images,
                completed_at: now()
            ])
        }
}
Key Features:
  • Namespace Assignment: Use namespace in the sharable to specify where the hand-off result will be stored
  • Terminal Route Result: The mobile workflow ends with terminal(payload) to pass data back
  • Await Command: await(namespace, timeout) pauses execution until the mobile workflow completes
  • Automatic Result Propagation: Terminal route result is automatically sent to the awaiting execution
  • Timeout Handling: Use onTimeout block to handle cases where the mobile workflow isn’t completed in time
  • Result Access: After await completes, access the result via the namespace variable (e.g., handoff)
Use Cases:
  • Mobile biometric verification during web onboarding
  • Photo/document capture on mobile device
  • Mobile signature collection
  • Two-factor authentication via mobile app
  • Mobile payment authorization
Flow Diagram:
  1. Main execution creates sharable with namespace and starts await
  2. Mobile device receives sharable token and executes workflow independently
  3. Mobile workflow terminates with terminal(result_data)
  4. Result is automatically propagated to main execution’s namespace
  5. Main execution resumes and accesses result via namespace variable

Configuration and Operations

Timeout Configuration

# application.yml
hub:
  sharable:
    operation-timeout: PT30S    # DynamoDB operations
    execute-timeout: PT1M       # Execution preparation

Monitoring and Metrics

Monitor these Kafka topics for sharable activity:
  • sharable-events - All sharable lifecycle events
  • sharable-requests - Internal processing requests
Key metrics to track:
  • Token creation rate
  • Token execution rate
  • Token expiration rate
  • Error rates by type
  • Average token lifetime

Storage Details

  • DynamoDB Table: sharable
  • Partition Key: token (string)
  • TTL Attribute: ttlEpochSeconds for automatic cleanup
  • State Store: Kafka Streams state store for expiration tracking

Best Practices

  1. Security
    • Use appropriate expiration times
    • Don’t log tokens in plain text
    • Consider token rotation for long-lived services
  2. Performance
    • Use reusable tokens for high-frequency access
    • Set reasonable expiration times to prevent storage bloat
    • Monitor token usage patterns
  3. Error Handling
    • Always handle 410 Gone responses for expired tokens
    • Implement retry logic for timeout errors
    • Log sharable events for debugging
  4. Integration
    • Use polling patterns for async operations
    • Implement proper webhook validation
    • Consider rate limiting for reusable tokens

Troubleshooting

Common Issues

Token Not Found (400 Bad Request)

  • Verify token exists and hasn’t been deleted
  • Check for typos in token value
  • Ensure proper URL encoding

Token Expired (410 Gone)

  • Check expireIn settings
  • Verify token hasn’t timed out
  • For single-use tokens, ensure execution hasn’t already completed

Timeout Errors (504 Gateway Timeout)

  • Check DynamoDB connectivity
  • Verify operation timeout configuration
  • Monitor Kafka topics for processing delays

Execution Errors

  • Verify component availability
  • Check function/workflow references
  • Review execution permissions and context