Carry changes back
Notice what people change in Marfa in a connector's items, carry those changes back to the vendor, and create at the vendor what people make in Marfa.
The items a connector pulls are ordinary items, and people and apps edit, archive and move them to the bin. A two-way connector notices those changes and carries them back to the vendor. The connector kit does all of this for you; the operations below do the same for a connector built directly on the API.
The examples follow a task manager connector with a key of its own under the source tasks, which writes the type tasks.task with link_field task_id. In the TypeScript examples, marfa is a TypeScript SDK client made with that key, connectorId is the registration's ID, and processName is the process's name (<process> in HTTP).
Watch for changes made in Marfa
To learn of changes made in Marfa, follow the event stream for your type:
GET /events?type=tasks.task
Authorization: Bearer <key>
Last-Event-ID: 11Each change arrives as an event with the item as it now stands. Last-Event-ID resumes after the last event you handled, so keep that cursor in the state document. Keep a copy in sync explains the stream.
Your own writes come back on the stream too. To tell a person's change from your own, keep an agreement for each item: what the connector and the vendor last agreed it holds. After every write you make or carry, write the agreement, under the connector's hold, as Holds and retries shows:
POST /connectors/01a12313-d219-7e92-8869-1f6546b43dbc/agreements
Authorization: Bearer <key>
Content-Type: application/json
{
"process": "<process>",
"set": [
{
"item_id": "01a12313-fd4a-78a1-af1e-32792c3df63b",
"waiting": false,
"record": { "title": "Renew passport", "status": "not_started" }
}
]
}When an event arrives, look up the item's agreement and compare:
- The item matches the
record: the change is one you already agreed, such as your own write. - The item differs: someone changed it in Marfa. Write the agreement again with
waitingset totrueand therecordunchanged, so it still says what was agreed and now says a change waits for the vendor.
Record the change before you move the cursor past its event. If the process stops between the two, the next run reads the same event again and finds the agreement waiting; the other way round, a stop loses the change.
Carry a change back
List the agreements that are waiting:
GET /connectors/01a12313-d219-7e92-8869-1f6546b43dbc/agreements?waiting=true
Authorization: Bearer <key>{
"data": [
{
"item_id": "01a12313-fd4a-78a1-af1e-32792c3df63b",
"waiting": true,
"record": { "title": "Renew passport", "status": "not_started" },
"updated_at": "2026-10-09T23:51:35.911Z"
}
],
"next_cursor": null
}For each one:
- Read the item.
- Send the vendor what changed.
- Once the vendor has taken it, write the agreement with
waitingset tofalseand arecordof what both sides now hold.
If the vendor refuses the change or doesn't answer, leave the agreement waiting, say why in the run's report, and try again on the next run. Never clear a waiting agreement to move on: the change would be forgotten.
Send a new item to the vendor
A person can also make an item of your type in Marfa, and a two-way connector creates it at the vendor. The type's link_field names the property that holds the vendor's ID for the record. Marfa keeps the value unique within the type, refusing a second item with it as 409 link_taken.
An item with no value there is one the vendor hasn't been told about. For each one:
- Create the record at the vendor, sending the item's
idas the vendor's idempotency key if it takes one, so a retry can't create it twice. - Update the item to set the link to the vendor's ID, sending the
versionyou read. - Write the item's agreement.
PATCH /items/01a12314-5e6a-7687-b04c-d62fa7beeccb
Authorization: Bearer <key>
Content-Type: application/json
{ "version": 1, "properties": { "task_id": "4020" } }Set the link straight after the vendor answers. A run that stops between the two sends the same create again, which the vendor's idempotency key answers with the record already made. Register a type describes link_field.
Rare cases
Settle a conflict
When a person changes a field in Marfa while the vendor changes the same field, both the item and the vendor differ from the agreement's record. One side has to win, and Marfa doesn't decide which: choose a rule and apply it every time.
The kit decides by time. The later change wins, comparing when the vendor says it changed the record with when the change in Marfa was first seen, and a tie goes to Marfa. A field that changed on only one side takes that side's value.
Carry a purge to a vendor that can't delete
Change the record instead, such as by closing the task, then settle the tombstone to the time of that change. Otherwise the next pull reads the change and brings the record back as a new item.
The rules behind this page are in the connectors and items chapters of the contract.