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:
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:
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:
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:
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
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>"}' --waitBehavior and errors
| Case | Behavior |
|---|---|
| Another connection, environment, integration or reference type | REFERENCE_NOT_FOUND |
| Invalid private identity | INVALID_REFERENCE |
| Existing reference definition's schema changes | REFERENCE_SCHEMA_CHANGED; use a new filename or perform an explicit migration |
| Connection disconnected | Mappings are deleted and further reference writes are fenced |
| Record removed from a sync | Mapping remains; the connector checks whether the upstream object still exists |
| MFA suspends execution | Resolution 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.