v1.0.0
Runtime workers
Host your own HTTPS worker and receive signed requests from TIMES.
mode: "runtime" guests host their own HTTPS worker. TIMES signs outbound requests and never stores partner OAuth client secrets in the catalog; connection tokens stay in connection_secrets.
Manifest
{
"schemaVersion": "1",
"slug": "my-worker-app",
"kind": "app",
"version": "0.1.0",
"name": "My Worker App",
"mode": "runtime",
"ui": {
"framework": "next",
"runtime": "iframe",
"surfaces": ["page"],
"entry": "./out/index.html",
"uses_times_tokens": true
},
"scopes": { "required": ["state:read", "state:write", "events:subscribe"] },
"i18n": {
"locales": ["en", "am", "om", "ti"],
"catalogs": {
"en": "./i18n/messages/en.json",
"am": "./i18n/messages/am.json",
"om": "./i18n/messages/om.json",
"ti": "./i18n/messages/ti.json"
}
},
"runtime": {
"workerUrl": "https://worker.example.com",
"webhookPath": "/webhook",
"signing": { "algorithm": "hmac-sha256", "header": "X-Times-Signature", "skewSeconds": 300 }
}
}Signing
TIMES posts to {workerUrl}{path} with:
X-Times-Timestamp: unix secondsX-Times-Signature: hex HMAC-SHA256 of{timestamp}.{body}X-Times-Slug: catalog slug
Reject requests outside skewSeconds. Configure the shared secret via ERP Apps:Workers:{slug}:SigningSecret or Apps:Workers:DefaultSigningSecret.
Host endpoints
POST /api/v1/apps/integrations/worker/invoke{ slug, payload }POST /api/v1/apps/integrations/webhook/test{ slug, path?, payload? }POST /api/v1/apps/integrations/sync/run{ slug, payload? }
Guest iframe sessions can mint a short-lived provider token via POST /api/v1/apps/runtime/v1/provider/token when a platform OAuth connection exists on the same slug.
Verifying a signature
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(body: string, timestamp: string, signature: string, secret: string) {
const age = Math.abs(Date.now() / 1000 - Number(timestamp));
if (age > 300) return false;
const expected = createHmac('sha256', secret).update(`${timestamp}.${body}`).digest('hex');
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}Find your signing secret on the AIP detail page under Runtime.



