Agents

Connected sites

How a website outside Braaand keeps its own copy of the brand's people, company details and offices, and stays in step.

Some facts belong in two places. The brand's people, its company details and its offices are used in Braaand for ads and for the sites built here, and they are used on the live website for contact pages, profile pages and search. A connected site is a website in Polyther that keeps its own copy of those records and stays in step with Braaand.

The copy is a real copy. The website serves its pages from its own database, so it keeps working if Braaand cannot be reached.

Connecting a site

Under Brand settings → Connected sites, choose Connect a site, give it a name and the Polyther site's slug, and choose what it may do:

  • Read only. It gets every change. It cannot change anything in Braaand.
  • Master. It also sends its own changes back. A brand has one master, and the choice cannot be changed later.

Connecting mints a key, shown once. Paste it into the site. Disconnecting a site makes its key stop working at once; the site keeps the copy it has.

One master is a rule, not a limit waiting to be lifted. Two sites that could both write would overwrite each other's changes through Braaand, and nobody would be told.

What a connected site reads

Only what may leave Braaand. A field marked private on a person or on the company is not in what a connected site reads, and neither is a private portrait. An archived person is still there, marked archived, because the site has to hear that they left. A person who was erased arrives as a note saying only that they are gone.

Each change arrives as the record as it is now, never as a list of edits. Reading the same change twice is harmless.

When the same detail changes in two places

The master site can send its own changes back. Braaand compares them field by field. A change to a field that was not touched here since the site last looked simply applies, even when other fields of the same person changed here in the meantime. When the same field was changed in both places, neither value is overwritten. Both wait under Changed in two places at the top of the People page (or the Company page), each shown with where it came from, and someone chooses which one stays. The same choice can be made on the site. Two places setting a field to the same value is not a conflict.

A few things a site can never send back: a portrait, a person's address, which fields are private, a value for a field that is private here, and anything containing markup. Removing someone for good is done in Braaand by an admin.

Being told when something changes

A connected site can give an address to be notified at. When a person, the company or an office changes, Braaand sends that address a short signed message saying only that something changed, and the site fetches the changes. Several edits in a row become one message. If the address cannot be reached, Braaand tries again over about forty minutes and shows the last failure on the site's row under Brand settings. The site also checks on its own every few minutes, so a missed message only costs a little time.

For developers and agents

Route What it answers
GET /api/brands/{brandId}/records A snapshot: every shared record with its public fields, the revision to push against, a person's stored address and portrait, plus a cursor and a field schema. ?kinds=person,company,office narrows it.
GET /api/brands/{brandId}/records/changes?since=<cursor> What changed since the cursor: one entry per record, carrying its current state, or erase. 410 resnapshot when the cursor is not on this brand's history.

CLI: braaand records snapshot <brandId> and braaand records changes <brandId> --since <cursor> show exactly what a connected site sees.

| POST /api/brands/{brandId}/records/push | The master site's edits, merged field by field. Only the master's own key may call it. | | GET /api/brands/{brandId}/records/conflicts | Fields changed in both places, with both values. | | POST …/records/conflicts/{conflictId}/resolve | Settle one: { fields: { phone: "braaand" } } keeps Braaand's value, "site" takes the site's. |

Agents: list_record_conflicts and resolve_record_conflict. Show the user both values and let them choose; never settle a conflict on your own judgment. CLI: braaand records conflicts and braaand records resolve.

The full contract, including the change notification, is docs/records-sync-contract.md in the repository.