Share Data From Your Backend


KV sharing is in beta. Method shapes, limits, and behavior may change between releases.

Some apps share data without the user clicking a share button. A team chat app can share each new room with the whole team as soon as it's created, or a backend can add a member to a room when your own rules say so. In both cases your app calls puter.kv.share() for the user. The user gives your app permission once, and after that it shares without a prompt each time.

Ask for Permission Once

Ask with puter.perms.request(), naming the part of your app's data the shares will cover and the highest access level they'll give:

await puter.perms.request('kvShare', { prefix: 'room:', access: 'write' });

The user sees a prompt asking whether your app may let other Puter users it picks read and change the room: part of its data. The call resolves to true once they allow it, and later calls resolve to true without asking again.

Sharing outside the prefix or above the level is rejected with events_kv_handle_not_delegated. After the user withdraws the permission, your app gets the same error when it shares or stops a share under that prefix, and the shares it already made stay in place.

Share From Your App

Once the user allows it, your app can share any room under room: with no prompt. This creates a room and shares it with everyone on the team in one call:

const team = ['alice', 'bob', 'carol'];
const prefix = `room:${crypto.randomUUID()}:`;

await puter.kv.set(`${prefix}meta`, { title: 'Launch planning' });
const results = await puter.kv.share({
    prefix,
    recipients: team,
    access: 'write',
    label: 'Launch planning',
});

The call resolves to one item per recipient. A recipient your app couldn't share with, such as a username that doesn't exist, has an error on their item, and the others still get the share.

Share From a Worker

A worker can share for the user too, which keeps the logic on your server. In a worker, user.puter acts as the user who called it, from the app that called it. So the share is of your app's data in the user's account, and the permission from the first step applies:

router.post('/rooms/:roomId/members', async ({ request, params, user }) => {
    const { username } = await request.json();
    const share = await user.puter.kv.share({
        prefix: `room:${params.roomId}:`,
        recipient: username,
        access: 'write',
        label: 'Launch planning',
    });
    return { holder: share.holder, access: share.access };
});

Your app calls it with puter.workers.exec(), which sends the user's token so the worker gets user.puter. In this call, workerUrl is your worker's URL:

const response = await puter.workers.exec(`${workerUrl}/rooms/${roomId}/members`, {
    method: 'POST',
    body: JSON.stringify({ username: 'dave' }),
});
const added = await response.json();

In the same worker, me.puter is your own account as the worker's owner, so it can't share the user's data.

The permission belongs to your app, not to the worker, so your app can still share directly. A check in the worker only applies to the shares that go through the worker.

The holder finds these shares with puter.kv.listShared() in your app, as in Share a List With Someone, and needs no permission to use them.

← All recipes