ByteChef LogoByteChef
EmbeddedInitial Setup

Syncing Connected Users

How a Connected User record comes into existence, what an external user id is, and how to attach your own name, email and metadata to it.

A Connected User is ByteChef's record of one of your end users. "Syncing" is the natural word for keeping it current, but it describes the wrong shape: there is no push, no bulk import, and no create endpoint. ByteChef learns that a user exists the first time that user authenticates, and everything you know about them - their name, their email, your own identifiers - you attach afterwards.

This page covers the identity contract, when the record appears, and how to enrich it.

The external user id

Every Connected User is keyed by an external user id: an identifier your application owns and ByteChef stores verbatim. ByteChef never generates it and never interprets it, so the uniqueness and stability guarantees are entirely yours to make.

It reaches ByteChef by one of two routes, depending on how the call is authenticated:

CredentialWhere the id comes from
Signing Key JWTThe token's sub claim - see Installing the SDK.
API KeyThe {externalUserId} segment of the request path.

Both routes converge on the same lookup, so a user your frontend created through the Connect dialog and a user your backend addresses by path are the same record.

Pick an id that never changes

Use your internal primary key, not an email address or username. Changing the external user id does not rename the Connected User - it creates a second, empty one, and the original user's connections and enabled workflows stay behind on the old id.

The record appears on first contact

There is no "create connected user" call. The first request that successfully authenticates for a given external user id creates the record, and every later request for that id reuses it. Opening the Connect dialog is usually what triggers it, but any authenticated embedded API call does.

This has one consequence worth designing around: you cannot pre-provision users. Signing up a user in your product does not produce a Connected User in ByteChef, and there is no endpoint that would let you create one ahead of time.

One record per environment

The lookup is scoped to the environment the request carries, so the same external user id in Development and in Production is two independent records - separate connections, separate enabled workflows, separate metadata. Requests select the environment with the optional X-Environment header (DEVELOPMENT, STAGING, PRODUCTION); with the header omitted, ByteChef uses PRODUCTION.

Promoting a user's setup from one environment to another is not something the connected-user API does - each environment's record is built by that environment's own traffic.

Attaching your own data

A freshly created record holds only the external user id. To give it a name, an email, or your own fields, PATCH it:

EndpointCredentialIdentifies the user by
PATCH /api/embedded/v1/meSigning Key JWTthe token's sub claim
PATCH /api/embedded/v1/{externalUserId}API Keythe path segment

Use /me from a surface that already holds an end-user JWT, and /{externalUserId} from your backend when you are reconciling users in bulk. Both take a flat JSON object and return 204 No Content.

await fetch(`${baseUrl}/api/embedded/v1/${externalUserId}`, {
    method: 'PATCH',
    headers: {
        Authorization: `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
        'X-Environment': 'PRODUCTION',
    },
    body: JSON.stringify({
        name: 'Ada Lovelace',
        email: 'ada@example.com',
        plan: 'enterprise',
        accountId: 'acct_1234',
    }),
});

What happens to each key

The body is a free-form object, but the keys are not all treated alike, and the rules are easy to trip over:

  • name and email become real fields. They are matched case-insensitively and are the two columns shown in the Connected Users table. They only get this treatment when the value is a string - {"name": 42} falls through and is stored as metadata instead.
  • Everything else becomes metadata, as a string. Metadata values are stored as text, so a number, boolean, or nested object is converted with its string form. Send {"seats": 5} and you get back "5"; send an object and you get its serialized form rather than a structure you can query. Flatten anything nested yourself, and keep values small.
  • Metadata merges - it never replaces. Keys you send are added or overwritten; keys you omit are left alone. A PATCH is always additive.
  • You cannot delete a metadata key. Sending null for a key does not clear it - null values are discarded before the merge, so the previous value survives. Write a sentinel your application understands (an empty string, say) if you need to represent "unset".

PATCH fails before the user's first authenticated call

The update resolves an existing record; it does not create one. Calling it for an external user id that has never authenticated is an error, not an upsert. Enrich a user after they first connect - not when they sign up in your product.

When to call it

Two moments cover most integrations:

  1. Just after the user's first connection succeeds, when you know the record now exists. This is where the initial name and email belong.
  2. Whenever the underlying profile changes in your product - a rename, a plan change, a new account id - so the Connected Users table and anything keyed off metadata stay truthful.

Because the endpoint is additive and cheap, a small reconciliation job that re-PATCHes the users you know are connected is a reasonable safety net. There is no bulk variant, so it is one request per user.

Disabled users

An admin can disable a Connected User from the console. A disabled user's requests are rejected at authentication - the Connect dialog will not open for them and their workflows stop executing - until they are re-enabled. The record, its connections, and its metadata all survive; only access is withdrawn.

See Connected Users for the console side: the table, the detail sheet, and the enable, disable, and delete actions.

How is this guide?

Last updated on

On this page