App events
Subscribe to connected app changes and deliver them to your server.
Arc0 polls supported app events through an authorized connected account, stores each event, and delivers it to the destinations you choose. Start in Events in your dashboard. GitHub issue creation is the first supported event. App catalog coverage does not imply event coverage.
Set up a subscription
- Choose an event and a connected account in Subscriptions. Business projects also select the end user; personal organizations always use your own account.
- Enter the repository owner and name, then create the subscription.
- Add an HTTPS endpoint under Destinations. Copy its signing secret immediately and store it on your server. Arc0 does not show the secret again.
- Create a route from the subscription to your destination.
- Inspect attempts and status under Deliveries.
Polling runs approximately once per minute. Subscriptions deliver newly observed issues; they do not import existing repository history. Event payloads are retained for seven days.
Pause a subscription to stop polling without removing its configuration. Archive subscriptions that you no longer need. Disabling a destination or route stops its delivery path.
TypeScript
In API keys, choose Manage event subscriptions and deliveries to create a key with
events:manage permission. Use that key with the server-side SDK. Keep the key and
signing secret out of browser code.
const subscription = await arc0.createEventSubscription({
triggerId: "github.issue.created",
triggerVersion: "1",
connectionId: "your-connection-id",
endUserId: "your-end-user-id",
config: { owner: "your-org", repo: "your-repo" },
name: "New support issues",
});
const endpoint = await arc0.createEventEndpoint({
name: "Support backend",
url: "https://your-server.example/events",
});
// Store endpoint.signingSecret securely; it is returned only on creation or rotation.
await arc0.createEventRoute({ subscriptionId: subscription.id, endpointId: endpoint.id });
const deliveries = await arc0.listEventDeliveries({ subscriptionId: subscription.id });Python
subscription = arc0.create_event_subscription(
trigger_id="github.issue.created", trigger_version="1",
connection_id="your-connection-id", endUserId="your-end-user-id",
config={"owner": "your-org", "repo": "your-repo"},
)
endpoint = arc0.create_event_endpoint(name="Support backend", url="https://your-server.example/events")
arc0.create_event_route(subscription_id=subscription["id"], endpoint_id=endpoint["id"])A client configured with an end-user binding keeps that binding on event requests. Server permissions remain authoritative; changing a request field cannot widen access.
Verify delivery signatures
Verify the exact raw request body before parsing JSON. Do not serialize a parsed object for verification. Both SDKs accept a map of signing versions to secrets and enforce a five-minute timestamp window with a constant-time signature comparison.
import { verifyEventSignature } from "@arc0/sdk";
const valid = verifyEventSignature({
rawBody, // raw string or Uint8Array from the HTTP request
headers, // string header values, including arc0-signature and arc0-timestamp
secrets: { "1": process.env.ARC0_EVENT_SIGNING_SECRET! },
});
if (!valid) throw new Error("Invalid event signature");
// Parse only after verification, then deduplicate the event ID transactionally.from arc0 import verify_event_signature
valid = verify_event_signature(raw_body=raw_body, headers=headers, secrets={"1": signing_secret})The signed bytes are eventId.timestamp.rawBody, using HMAC-SHA256 with the secret as UTF-8
and a hexadecimal signature. Headers are arc0-event-id, arc0-timestamp (Unix seconds),
arc0-signature, and arc0-signing-version. arc0-delivery-id and
arc0-delivery-generation identify the attempt stream; they are informational, not part of the
signature. Use the verified event ID to deduplicate side effects.
The JSON envelope contains id, type, version, projectId, subscriptionId, endUserId,
occurredAt and data. The GitHub issue event includes repository and issue identifiers,
title, URL, state and timestamps. Treat issue text as untrusted application data.
Delivery and replay
Delivery can happen more than once. Deduplicate event IDs on your server before applying a side effect. Review the attempt history before replaying a failed delivery. Replay creates a new delivery generation for the same event; it does not extend payload retention. Reading an expired payload returns HTTP 410.
Secret rotation returns the new secret once and the previous secret’s validity deadline. Keep the previous version available for already queued deliveries during the rotation window.
App events are separate from connection-health notifications, which report when a connected account needs attention or recovers.