browserlayer / docsPrivate alphaDashboard ↗

Start here

Websites → endpoints and syncsInstallRun your first endpointAtlas walkthrough

Build integrations

Project structureCapture a websiteBuild with your coding agentEndpointsSteps and connector flowsTyped referencesAuthenticationUser input and MFABrowser interactions and HTTPSync recordsCheckpoints and deletionsTest an integrationDebug in Studio

Use in your app

Connect accounts with hosted LinkCall an endpointConsume sync recordsReceive webhooks

Deploy

Deploy and verify

Reference

SDK referenceHTTP APICLI reference
Build integrationsMarkdown

Typed references

Give your app stable IDs without exposing the website's IDs. List or sync records, then pass their IDs to another endpoint.

Define the private identity

connector/references/booking.ts:

ts
import { createReference } from "@browserlayer/sdk";
import { z } from "zod";

export default createReference(
  z.object({
    id: z.string().regex(/^[\w-]{1,80}$/),
  }),
);

The filename identifies the reference type. Its schema describes stable upstream identity. Keep cookies, form tokens and temporary URLs out of it.

Keep models ordinary

models/booking.ts:

ts
import { z } from "zod";
import bookingReference from "../connector/references/booking.js";

export const Booking = z.object({
  id: bookingReference.id,
  destination: z.string(),
  date: z.string(),
  status: z.enum(["confirmed", "cancelled"]),
  amount: z.number(),
});

bookingReference.id validates the public ID format. It performs no lookup. Other models can use it too, for example bookingId: bookingReference.id on a refund.

Create public IDs

Inside a listing endpoint or sync:

ts
const id = await ctx.references.store(bookingReference, { id: row.id });
const record = Booking.parse({
  id,
  destination: row.destination,
  date: row.date,
  status: row.status,
  amount: row.amount,
});

row is the connector's upstream booking. Repeated stores of the same private identity return the same public ID within the connection, integration, environment and reference type. List and sync use the same definition and therefore the same IDs.

Await stores sequentially, including inside loops. Reference operations participate in durable replay. Storing an identity does not publish a sync record or send a webhook.

Resolve before acting

Inside an endpoint that accepts bookingId: bookingReference.id:

ts
const reference = await ctx.references.resolve(bookingReference, bookingId);
await ctx.run(cancelBooking, reference);
return { bookingId, cancelled: true };

See the complete cancellation endpoint. Return the public ID, not the internal flow's upstream ID.

Try Atlas

sh
browserlayer run atlas-demo list-bookings --local \
  --connection <connection-id> --input '{}' --wait

browserlayer run atlas-demo cancel-booking --local \
  --connection <connection-id> \
  --input '{"bookingId":"<id-from-list-or-sync>"}' --wait

Behavior and errors

CaseBehavior
Another connection, environment, integration or reference typeREFERENCE_NOT_FOUND
Invalid private identityINVALID_REFERENCE
Existing reference definition's schema changesREFERENCE_SCHEMA_CHANGED; use a new filename or perform an explicit migration
Connection disconnectedMappings are deleted and further reference writes are fenced
Record removed from a syncMapping remains; the connector checks whether the upstream object still exists
MFA suspends executionResolution replays its saved result in the same run

Private mappings are encrypted at rest. Resolve results are redacted from reference-operation inspection. Integration authors must still avoid returning or logging private values; internal connector steps and network inspection can show upstream data to operators.

Your backend must authorize the user's connection. An opaque ID is not permission to act, and a successful resolution does not prove the booking is still cancellable.

← Steps and connector flowsAuthentication →
On this pageDefine the private identityKeep models ordinaryCreate public IDsResolve before actingTry AtlasBehavior and errors