Voyage Agent Layer

Use this skill when an agent needs to provision API credentials, create or publish a journey, or build a custom registration portal against Voyage.

Guardrails

  • Do not use real customer tenants for experiments.
  • Do not print, store in docs, or commit API-key secrets. Use placeholders such as vyg_live_xxx.
  • API keys cannot mint other keys. API-client administration requires a live identity session cookie for a tenant owner/admin or platform admin.
  • Current admin journey routes require journey.build; do not assume journey.read is enough for /v1/journeys.
  • Public journey registration routes are public so browser flows can submit. Server-side agents should still send a key with registration.write.

Required Inputs

Collect or infer:

  • apiBaseUrl: edge gateway URL.
  • identityBaseUrl: identity worker URL for API-client admin.
  • tenantSlug.
  • Admin session cookie, only when minting a client/key.
  • Target event ID for registration-backed journeys.
  • Journey type: appointment, event, or queue.
  • Registration portal host/runtime choice.

Credential Workflow

  1. Create an API client:
curl -sS \
  -X POST "$IDENTITY_BASE_URL/t/$TENANT_SLUG/admin/api-clients" \
  -H "content-type: application/json" \
  -H "cookie: identity_session=..." \
  --data '{"name":"Agent journey builder","description":"Journey and registration portal automation"}'
  1. Mint a key:
curl -sS \
  -X POST "$IDENTITY_BASE_URL/t/$TENANT_SLUG/admin/api-clients/client_xxx/keys" \
  -H "content-type: application/json" \
  -H "cookie: identity_session=..." \
  --data '{"scopes":["journey.build","registration.write"]}'
  1. Store the one-time secret in the agent runtime secret manager as VOYAGE_API_KEY.

  2. For rotation, call:

curl -sS \
  -X POST "$IDENTITY_BASE_URL/t/$TENANT_SLUG/admin/api-clients/client_xxx/keys/key_xxx/rotate" \
  -H "content-type: application/json" \
  -H "cookie: identity_session=..." \
  --data '{"scopes":["journey.build","registration.write"]}'

Journey Build Workflow

Use @voyage/sdk and @voyage/journey-runtime/core.

import { createVoyageClient } from "@voyage/sdk";
import { defaultJourneyConfig } from "@voyage/journey-runtime/core";

const voyage = createVoyageClient({
  baseUrl: process.env.VOYAGE_API_BASE_URL ?? "https://api.example.com",
  tenantSlug: process.env.VOYAGE_TENANT_SLUG ?? "acme-events",
  apiKey: process.env.VOYAGE_API_KEY ?? "vyg_live_xxx",
});

const config = defaultJourneyConfig("event");

const draft = await voyage.journeys.create({
  type: "event",
  slug: "private-preview",
  eventId: "evt_123",
  config: {
    ...config,
    name: "Private preview",
    _eventId: "evt_123",
  },
});

const live = await voyage.journeys.publish(draft.journeyId, {
  version: draft.version,
  publishTargets: draft.configJson.publishTargets,
});

Registration Portal Workflow

Backend prerequisites:

  • Tenant is active and resolvable by path or domain.
  • Tenant config DB has feature_catalog.feature_key = 'journey_builder' for admin journey build calls.
  • Tenant ops DB has an events_studio_events row for the target event.
  • The journey row has eventId or config _eventId set to that event ID.

Portal server action:

const result = await voyage.journeys.submitPublicRegistration("private-preview", {
  selectedTickets: [{ tierId: "general", quantity: 1 }],
  guest: {
    firstName: form.firstName,
    lastName: form.lastName,
    email: form.email,
    phone: form.phone,
  },
  answers: form.answers,
  agreedToTerms: true,
  surfaceType: "journey",
});

const confirmed = await voyage.journeys.getPublicRegistration(
  "private-preview",
  result.registration.guestId,
  result.registration.email,
);

Do not put VOYAGE_API_KEY in browser JavaScript. The portal browser posts to the portal backend; the backend calls Voyage.

Data Structures To Expect

The registration path writes and updates these tenant ops tables:

  • events_studio_guests: one row per captured registration.
  • events_studio_events: registration and attendance counts are refreshed after writes.
  • events_studio_page_visits: optional visit tracking through /visit.

The journey path writes:

  • journey_surfaces: draft/live versions.
  • journey_staff_roster and journey_staff_booking_links: default staff links are inserted when missing.

Acceptance Check

Before telling a human a portal works:

  1. Publish the journey.
  2. Load the public journey by slug through the live host or intended API base.
  3. Submit a full registration through the portal backend.
  4. Read the registration back with getPublicRegistration.
  5. Confirm the returned event count increased or the row exists in tenant ops data.

Green typechecks or a rendered form are not enough.