# Agent Coordination

## Event-driven handoffs between agents[​](#event-driven-handoffs-between-agents "Direct link to Event-driven handoffs between agents")

*One agent writes, the other reacts — coordinated through object notifications with zero polling.*

Multi-agent systems often need one agent to hand off work to another. Agent A produces an artifact — a report, a processed dataset, an intermediate result — and Agent B needs to pick it up and continue. Polling the bucket wastes API calls and adds latency. Shared queues add infrastructure you have to run.

[Tigris Object Notifications](/docs/buckets/object-notifications/.md) turn the bucket itself into the coordination layer. When Agent A writes or updates an object, Tigris fires an HTTP `POST` to your webhook endpoint with the bucket name, key, size, and ETag. Your handler dispatches the event to Agent B, which reads the new object and takes its next step.

[Object notifications →](/docs/buckets/object-notifications/.md)

### Benefits[​](#benefits "Direct link to Benefits")

No polling, no queues

Object notifications push events the moment a write completes. Agent B doesn't need to poll on a timer, and you don't need to stand up a message queue between the two agents. The bucket is the shared state and the event source.

Prefix-scoped triggers

Filter notifications to specific key prefixes so Agent B only wakes up for the objects it cares about. If Agent A writes scratch files to `tmp/` and final outputs to `results/`, you can target only `results/`.

```
WHERE `key` REGEXP "^results/" AND `Event-Type` = "OBJECT_CREATED_PUT"
```

Loose coupling

Agent A doesn't need to know Agent B exists. It writes to the bucket using standard S3 operations. The notification rule handles routing. You can add a third agent, swap out Agent B, or fan out to multiple consumers without changing Agent A's code.

Built-in delivery metadata

Every webhook payload includes the bucket, object key, size, ETag, and event timestamp. Agent B has everything it needs to fetch the right object and decide whether to act — no extra API calls to discover what changed.

### Pattern: writer agent → watcher agent[​](#pattern-writer-agent--watcher-agent "Direct link to Pattern: writer agent → watcher agent")

Agent A writes artifacts to a shared bucket. A notification rule fires a webhook when new objects land under `results/`. The webhook handler dispatches the event to Agent B, which reads the object and continues the workflow.

#### 1. Configure the notification[​](#1-configure-the-notification "Direct link to 1. Configure the notification")

Set up an object notification rule pointing at your webhook endpoint. Filter to the prefix you care about. Tigris supports [basic and bearer token authentication](/docs/buckets/object-notifications/.md#webhook-authentication) on webhook endpoints — enable this so only Tigris can trigger your handler.

```
tigris buckets set-notifications coordination-bucket \

  --url https://example.com/webhook \

  --filter 'WHERE `key` REGEXP "^results/"' \

  --token "$WEBHOOK_SECRET"
```

#### 2. Agent A writes artifacts[​](#2-agent-a-writes-artifacts "Direct link to 2. Agent A writes artifacts")

Agent A writes its output using the [Tigris SDK](/docs/sdks/tigris/.md) — no awareness of the downstream consumer needed.

```
import { put } from "@tigrisdata/storage";

import { readFile } from "node:fs/promises";



const report = await readFile("./report.json");



await put("results/report.json", report, {

  config: { bucket: "coordination-bucket" },

});
```

#### 3. Webhook handler dispatches to Agent B[​](#3-webhook-handler-dispatches-to-agent-b "Direct link to 3. Webhook handler dispatches to Agent B")

Your handler receives the notification and kicks off Agent B with the object details.

```
import express from "express";



const app = express();

app.use(express.json());



app.post("/webhook", (req, res) => {

  try {

    for (const event of req.body.events) {

      const bucket = event.bucket;

      const key = event.object.key;



      // Dispatch to Agent B with the object reference.

      // Replace with your dispatch logic: queue push, function call, HTTP request, etc.

      triggerAgentB({ bucket, key });

    }



    res.sendStatus(200);

  } catch (err) {

    console.error("Failed to process webhook:", err);

    // Return a non-200 status so Tigris retries delivery

    res.sendStatus(500);

  }

});
```

#### 4. Agent B reads and acts[​](#4-agent-b-reads-and-acts "Direct link to 4. Agent B reads and acts")

Agent B fetches the object that triggered the notification and takes its next step — validation, transformation, forwarding, or anything else.

```
import { get, put } from "@tigrisdata/storage";



async function agentBHandler(bucket: string, key: string) {

  const result = await get(key, "string", { config: { bucket } });



  if (result.error) {

    console.error("Failed to fetch object:", result.error);

    return;

  }



  // Process the artifact

  const processed = process(result.data);



  // Optionally write output for a third agent

  await put(key.replace("results/", "processed/"), processed, {

    config: { bucket },

  });

}
```

note

Notifications are delivered at least once — Tigris retries on non-200 responses — and can arrive out of order across regions. Use the `Last-Modified` timestamp on the object (not `eventTime`) to sequence events correctly, and design your handlers to be idempotent.
