Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,22 @@ one sync run stripe --full-refresh

Change hooks (`onInsert`, `onUpdate`, `onChange`) fire per-page during sync — pipe to a shell command, a flow, or an event log. Root-array responses (e.g. Hacker News `/v0/topstories.json` → `[9129911, 9129199, ...]`) are supported by setting `resultsPath` to `""`, `"$"`, or `"."`; primitive elements are auto-wrapped as `{ [idField]: value }`. Run `one guide sync` for the full reference.

### `one relay`

Receive webhooks from platforms and forward them to any connected platform via passthrough actions.

```bash
one relay platforms # List relay-capable platforms + event type counts
one relay event-types <platform> # List supported event types for a platform
one relay create --connection-key <key> --create-webhook --event-filters '["event.type"]'
one relay activate <id> --actions '<json>' # Attach passthrough forwarding actions
one relay list # List existing relay endpoints
one relay events --platform <p> # Inspect received events
one relay deliveries --endpoint-id <id> # Check delivery status
```

Start with `one relay platforms` to discover which platforms support relay at all, then drill into `event-types <platform>` for the specific events. Run `one guide relay` for the full reference including `--metadata` requirements per platform.

### `one guide [topic]`

Get the full CLI usage guide, designed for AI agents that only have the binary (no MCP, no IDE skills).
Expand Down
7 changes: 5 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@withone/cli",
"version": "1.41.0",
"version": "1.42.0",
"description": "CLI for managing One",
"type": "module",
"files": [
Expand Down
15 changes: 15 additions & 0 deletions skills/one/references/relay.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ one --agent connection list

Identify source (sends webhooks) and destination (receives forwarded data). Note both connection keys.

If you're unsure whether the source platform supports relay at all, list relay-capable platforms first:

```bash
one --agent relay platforms
```

This returns `[{ "platform": "<name>", "eventTypeCount": <n> }, ...]` for every platform that One can receive webhooks from. If your source isn't in the list, relay won't work for it.

### Step 2: Get event types

```bash
Expand Down Expand Up @@ -173,6 +181,13 @@ one --agent relay activate <relay-id> --actions '[{
}]'
```

## Discovery Commands

```bash
one --agent relay platforms # All relay-capable platforms + event type counts
one --agent relay event-types <platform> # Event types for a specific platform
```

## Management Commands

```bash
Expand Down
41 changes: 41 additions & 0 deletions src/commands/relay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,47 @@ export async function relayDeliveriesCommand(options: {
}
}

export async function relayPlatformsCommand(): Promise<void> {
const { apiKey } = getConfig();
const api = new OneApi(apiKey, getApiBase());
const spinner = output.createSpinner();
spinner.start('Loading relay-capable platforms...');

try {
const platforms = await api.listRelayPlatforms();

if (output.isAgentMode()) {
output.json({ platforms });
return;
}

spinner.stop(`${platforms.length} relay-capable platform${platforms.length === 1 ? '' : 's'} found`);

if (platforms.length === 0) {
console.log('\n No relay-capable platforms available.\n');
return;
}

console.log();
printTable(
[
{ key: 'platform', label: 'Platform' },
{ key: 'eventTypeCount', label: 'Event types', color: pc.dim },
],
platforms.map(p => ({ platform: p.platform, eventTypeCount: String(p.eventTypeCount) }))
);
console.log();
console.log(
pc.dim(
` Run ${pc.cyan('one relay event-types <platform>')} to see the full event list for a platform.\n`
)
);
} catch (error) {
spinner.stop('Failed to load relay platforms');
output.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}

export async function relayEventTypesCommand(platform: string): Promise<void> {
const { apiKey } = getConfig();
const api = new OneApi(apiKey, getApiBase());
Expand Down
9 changes: 9 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
relayEventGetCommand,
relayDeliveriesCommand,
relayEventTypesCommand,
relayPlatformsCommand,
} from './commands/relay.js';

import { registerSyncCommands } from './lib/sync/index.js';
Expand Down Expand Up @@ -103,6 +104,7 @@ program
one cache update-all Re-fetch fresh data for all cached entries

Webhook Relay:
one relay platforms List platforms that support relay + event type counts
one relay create Create a relay endpoint for a connection
one relay list List relay endpoints
one relay activate <id> Activate with passthrough actions
Expand Down Expand Up @@ -642,6 +644,13 @@ relay
await relayEventTypesCommand(platform);
});

relay
.command('platforms')
.description('List all platforms that support webhook relay, with their event type counts')
.action(async () => {
await relayPlatformsCommand();
});


// ── Sync Commands ──

Expand Down
4 changes: 4 additions & 0 deletions src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,10 @@ export class OneApi {
return this.requestFull({ path: '/webhooks/relay/event-types', queryParams: { platform } });
}

async listRelayPlatforms(): Promise<Array<{ platform: string; eventTypeCount: number }>> {
return this.requestFull({ path: '/webhooks/relay/platforms' });
}


async waitForConnection(
platform: string,
Expand Down
4 changes: 3 additions & 1 deletion src/lib/guide-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ Receive webhooks from platforms (Stripe, GitHub, Airtable, Attio, Google Calenda

**Quick start:**
\`\`\`bash
one --agent relay platforms # See relay-capable platforms
one --agent relay event-types <platform> # See available events
one --agent relay create --connection-key <key> --create-webhook --event-filters '["event.type"]'
one --agent relay activate <id> --actions '[{"type":"passthrough","actionId":"...","connectionKey":"...","body":{...}}]'
Expand Down Expand Up @@ -228,6 +229,7 @@ Webhook relay receives events from platforms (Stripe, GitHub, Airtable, Attio, G
## Commands

\`\`\`bash
one --agent relay platforms # List relay-capable platforms + event type counts
one --agent relay event-types <platform> # List available events
one --agent relay create --connection-key <key> --create-webhook --event-filters '["event.type"]'
one --agent relay activate <id> --actions '<json>' # Add forwarding actions
Expand All @@ -242,7 +244,7 @@ one --agent relay deliveries --endpoint-id <id> # Check delivery status

## Building a Relay

1. **Discover connections** — identify source and destination platforms
1. **Discover connections** — identify source and destination platforms. Use \`one --agent relay platforms\` to see which platforms support relay at all before digging into a specific one.
2. **Get event types** — \`one --agent relay event-types <platform>\`
3. **Get source knowledge** — understand the incoming webhook payload shape (\`{{payload.*}}\` paths)
4. **Get destination knowledge** — understand the outgoing API body shape
Expand Down
4 changes: 2 additions & 2 deletions src/lib/sync/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Command } from 'commander';
import * as output from '../output.js';
import { OneApi } from '../api.js';
import { getApiKey, getAccessControlFromAllSources } from '../config.js';
import { getApiKey, getApiBase, getAccessControlFromAllSources } from '../config.js';
import { discoverModels } from './models.js';
import { readProfile, writeProfile, writeDraftProfile, listProfiles, generateTemplate } from './profile.js';
import { syncModel } from './runner.js';
Expand Down Expand Up @@ -122,7 +122,7 @@ function getApi(): OneApi {
if (!apiKey) {
output.error('No API key configured. Run "one init" first.');
}
return new OneApi(apiKey);
return new OneApi(apiKey!, getApiBase());
}

// ── sync profiles (built-in) ──
Expand Down