Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.remlo.xyz/llms.txt

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

Auto-Payroll is Remlo’s “OAuth for money” — an employer signs one on-chain transaction that authorizes a Remlo-controlled access key with a periodic spending cap, and Remlo runs payroll automatically every cycle until the key expires or is revoked. Built on Tempo’s TIP-1011 access keys (T3 upgrade, April 2026).

When to use it

Auto-Payroll fits employers who:
  • Run payroll on a fixed cadence (daily, weekly, bi-weekly).
  • Don’t want to log in and sign every cycle.
  • Have a stable employee roster (the cron uses the active KYC-approved roster at run time, so adds and removals propagate).
It does not fit employers who need calendar-month periods (Tempo doesn’t support those yet — see Limitations below) or whose roster changes wildly cycle-to-cycle.

How it works

The mechanism is a TIP-1011 access key. The key is a fresh secp256k1 keypair generated by Remlo at the moment of authorization. The employer signs an authorizeKey(...) transaction at the Tempo AccountKeychain precompile (0xAAAA...) that says, in protocol terms:
“Until expiry, the key with public address 0xACCESS_KEY may spend at most perPeriodAmount of tokenAddress per periodSeconds from my treasury, but only by calling executeBatchPayroll(...) on PAYROLL_BATCHER_ADDRESS.”
The chain enforces this. Remlo holds the private key (encrypted at rest) and uses it to sign each cycle’s executeBatchPayroll transaction. If a cycle would exceed the cap, the chain reverts.
employer
  ↓ POST /api/employers/[id]/autopayroll  (Privy bearer)
Remlo
  ├─ generates secp256k1 keypair
  ├─ encrypts private key with AES-256-GCM (AUTOPAYROLL_ENCRYPTION_KEY)
  ├─ persists row in autopayroll_authorizations (status=draft)
  └─ returns authorize calldata
employer's wallet
  ↓ signs authorizeKey(accessKey, 0, restrictions)
Tempo
  ↓ on-chain confirmation
employer
  ↓ PATCH .../autopayroll/[authId]  { action: 'confirm', authorizeTxHash }
Remlo
  └─ flips row to status=active

cron tick (every 15 min)
  ↓ enumerate active rows
  ↓ for each row whose period_seconds elapsed since last run
  ├─ build payroll batch from active KYC-approved roster
  ├─ decrypt access key (AUTOPAYROLL_ENCRYPTION_KEY)
  ├─ sign + broadcast executeBatchPayroll(...)
  └─ stamp last_run_at + tx_hash on the row

API

All endpoints require Privy bearer auth as the employer owner.

GET /api/employers/[id]/autopayroll

Lists the employer’s authorizations, newest first.

POST /api/employers/[id]/autopayroll

{
  "perPeriodAmount": "5000000000",   // base units; 6 decimals → $5000
  "periodSeconds": 604800,            // 1 week
  "expiresAtUnix": 1736294400,        // optional; omit / 0 → non-expiring
  "token": "0x20c0...",               // optional; defaults to pathUSD
  "notes": "Q2 weekly contractor payouts"
}
Returns the calldata the employer signs:
{
  "id": "...",
  "accessKeyAddress": "0x...",
  "authorizationCalldata": "0x980a6025...",
  "authorizationTarget": "0xAAAAAAAA00000000000000000000000000000000",
  "perPeriodAmount": "5000000000",
  "periodSeconds": 604800,
  "tokenAddress": "0x20c0...",
  "payrollBatcher": "0x90657d...",
  "payrollSelector": "0x..."
}

PATCH /api/employers/[id]/autopayroll/[authId]

State transitions:
  • { action: 'confirm', authorizeTxHash } — flip draftactive after on-chain confirmation
  • { action: 'pause' } / { action: 'resume' } — toggle without touching chain
  • { action: 'revoke', revokeTxHash } — record the on-chain revoke tx

DELETE /api/employers/[id]/autopayroll/[authId]

Returns the calldata the employer signs to revoke. Server does not broadcast — the chain authority sits with the employer.

Cron handler

/api/cron/autopayroll-tick (vercel.json schedule: */15 * * * *). Per active row whose last_run_at + period_seconds <= now:
  1. Pull employer + active KYC-approved roster.
  2. Build (recipients[], amounts[], memos[], employerAccountId) for PayrollBatcher.executeBatchPayroll.
  3. Decrypt access key, sign, broadcast.
  4. Record last_run_status + last_run_tx_hash.
Idempotency: the cron stamps last_run_at BEFORE broadcasting so a concurrent tick sees the updated timestamp and skips.

Schema

autopayroll_authorizations table — see db/migrations/20260509g_autopayroll.sql. Status state machine: draft → active → paused/active → revoked|expired|failed. The encrypted private key envelope:
{
  "v": 1,
  "iv": "<base64 12 bytes>",
  "ct": "<base64 ciphertext + auth tag>"
}

UI

Employer dashboard at /dashboard/payroll/auto. Two-step compose flow (configure cap + period + duration → on-chain sign), authorization rows with pause / resume / revoke. Linked from /dashboard/payroll/new.

Limitations

  1. Fixed-window periods only. Tempo’s TIP-1011 supports 1 day and 1 week (any positive uint64 of seconds). Calendar-month periods land in a future T-upgrade. For monthly payroll, run weekly with 1/4 the amount, or wait for the upgrade.
  2. Single token per authorization. A row authorizes spend on exactly one TIP-20. Multi-currency payroll needs multiple authorizations.
  3. Recipient set is implicit. The cron uses the employer’s active roster at run time. Adds/removes propagate without re-authorization. The on-chain restriction is on the executeBatchPayroll selector + the PAYROLL_BATCHER_ADDRESS target — recipients per call are whatever Remlo passes.
  4. No multi-sig. The employer signs once with a single Privy-managed key. Multi-sig flows would require ERC-4337 / Safe-on-Tempo, neither of which is wired today.

Operator runbook

  • Lost AUTOPAYROLL_ENCRYPTION_KEY → all encrypted access keys are dead-letter. The cron will fail on every active row. Operator should: mark all active rows failed, notify employers to revoke + re-create. No funds are lost (the chain still recognizes the access-key public address; the employer’s revoke is independent of Remlo’s encryption key).
  • Cron stops firing → check Vercel cron logs. Confirm CRON_SECRET is set. Confirm rows have status='active' and last_run_at + period_seconds <= now.
  • A specific row stuck at failed → the chain reverted the last cycle (cap exceeded, recipient blocked by TIP-403, contract paused). Inspect last_run_error. The row stays in failed until operator pauses it manually or employer revokes + re-authorizes.

Security

  • Access-key signing is server-only. The decrypted private key never crosses the network boundary.
  • The chain enforces the cap. Remlo’s signing key cannot exceed the periodic limit even if compromised — the worst case is the per-period amount draining once.
  • Revocation is employer-side. Remlo cannot revoke the employer’s access key (it doesn’t have admin rights — only the employer’s root key does).

See also