TrigRun
Use Cases

Scheduled Usage Metering and Billing Sync

Automate hourly usage metering and billing synchronization with TrigRun. Keep Stripe, Paddle, or custom billing systems accurate.

Sync usage data to your billing provider every hour. TrigRun calls your metering endpoint, which aggregates consumption (API calls, storage, compute minutes) and reports it to Stripe, Paddle, or your own billing system.

The problem

Your SaaS charges based on usage — API calls, storage, seats, compute minutes. Usage data accumulates in your database, but your billing provider (Stripe Metering, Paddle) needs regular updates to generate accurate invoices. A missed sync means under-billing (lost revenue) or delayed invoicing (cash flow issues).

How it works with TrigRun

┌─────────────┐   hourly     ┌──────────────────┐             ┌──────────────┐
│   TrigRun   │ ────────────▶│ Your Metering    │ ───────────▶│   Stripe /   │
│  Scheduler  │  POST /sync  │    Endpoint      │  report     │   Paddle     │
└─────────────┘              └──────────────────┘  usage      └──────────────┘
       │                            │
       ▼                            ▼
  Execution log              Aggregate usage
  "synced 142 accounts,      from your database
   total: $3,247.50"         for the last hour

Create via API

curl -X POST https://api.trigrun.com/v1/jobs \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Hourly usage metering sync",
    "kind": "cron",
    "schedule": {
      "cron": "5 * * * *"
    },
    "request": {
      "url": "https://api.yourapp.com/billing/sync-usage",
      "method": "POST",
      "headers": {
        "Authorization": "secret://billing-api-key",
        "Content-Type": "application/json"
      },
      "body": {
        "period": "last_hour",
        "provider": "stripe"
      },
      "timeout_seconds": 120
    },
    "retry_policy": {
      "max_attempts": 3,
      "retry_on_statuses": [500, 502, 503, 429]
    }
  }'

Note: 5 * * * * runs at minute 5 of every hour — offset from the top of the hour to avoid contention with other hourly jobs.

Expected results

Successful sync:

FieldExample value
Status200 OK
Duration12,450 ms
Response{"accounts_synced": 142, "usage_events_reported": 8340, "total_billable": "$3,247.50", "period": "2026-03-16T04:00:00Z/2026-03-16T05:00:00Z"}

Your metering endpoint

# FastAPI example
@app.post('/billing/sync-usage')
async def sync_usage(request: UsageSyncRequest):
    # Aggregate usage for the period
    start, end = compute_period(request.period)
    usage = await db.usage_events.aggregate(
        where={'timestamp': {'gte': start, 'lt': end}},
        group_by='workspace_id',
        sum='quantity',
    )

    # Report to Stripe
    for workspace_usage in usage:
        stripe.billing.meter_events.create(
            event_name='api_calls',
            payload={
                'stripe_customer_id': workspace_usage.stripe_id,
                'value': str(workspace_usage.total),
            },
        )

    return {
        'accounts_synced': len(usage),
        'usage_events_reported': sum(u.total for u in usage),
        'total_billable': calculate_total(usage),
        'period': f'{start.isoformat()}/{end.isoformat()}',
    }

Common schedules

PatternExpressionUse case
Hourly (offset)5 * * * *Real-time metered billing
Every 15 min*/15 * * * *Near-real-time usage dashboards
Daily summary0 1 * * *End-of-day billing reconciliation
Monthly close0 2 1 * *Monthly invoice generation

On this page