# Sharing and Publishing

Learn how to share assets and boards with external stakeholders and publish boards to the web.

## Overview[​](#overview "Direct link to Overview")

Playbook offers two main ways to make your content accessible to others:

* **Shared Links**: Private, trackable links for specific assets or boards
* **Published Links**: Public web pages showcasing your boards

***

## Prerequisites[​](#prerequisites "Direct link to Prerequisites")

* **Access Token**: API token with sharing permissions
* **Organization Slug**: Your organization identifier
* **Asset/Board Tokens**: IDs of content you want to share

***

## Sharing Assets[​](#sharing-assets "Direct link to Sharing Assets")

Create temporary shared links for individual assets.

### Creating an Asset Shared Link[​](#creating-an-asset-shared-link "Direct link to Creating an Asset Shared Link")

```
curl -X POST "https://api.playbook.com/v1/my-org/assets/product-photo/share" \

  -H "Authorization: Bearer YOUR_TOKEN"
```

**Response:**

```
{

  "data": {

    "url": "https://playbook.com/s/abc123/product-photo"

  }

}
```

### JavaScript Example[​](#javascript-example "Direct link to JavaScript Example")

```
async function shareAsset(assetToken) {

  const response = await fetch(

    `https://api.playbook.com/v1/${ORG_SLUG}/assets/${assetToken}/share`,

    { method: "POST", headers: { Authorization: `Bearer ${ACCESS_TOKEN}` } },

  );



  const { data } = await response.json();

  return data.url;

}



// Usage

const shareUrl = await shareAsset("product-photo");

console.log("Share link:", shareUrl);
```

***

## Sharing Boards[​](#sharing-boards "Direct link to Sharing Boards")

Create shared links for entire boards, allowing external viewers to see all assets within.

### Creating a Board Shared Link[​](#creating-a-board-shared-link "Direct link to Creating a Board Shared Link")

```
curl -X POST "https://api.playbook.com/v1/my-org/boards/main-collection/share" \

  -H "Authorization: Bearer YOUR_TOKEN"
```

**Response:**

```
{

  "data": {

    "url": "https://playbook.com/s/def456/main-collection"

  }

}
```

### Board Sharing Features[​](#board-sharing-features "Direct link to Board Sharing Features")

Shared board links include:

* All assets in the board
* Nested subboards (if applicable)
* Asset metadata (title, description, tags)
* Comments (optionally)
* Download capabilities (based on permissions)

### Link Controls[​](#link-controls "Direct link to Link Controls")

Board share and publish requests accept the same optional controls:

```
curl -X POST "https://api.playbook.com/v1/my-org/boards/main-collection/share" \

  -H "Authorization: Bearer YOUR_TOKEN" \

  -H "Content-Type: application/json" \

  -d '{

    "expiration_date": "2026-09-01T12:00:00Z",

    "disable_downloads": true,

    "password": "client-review"

  }'
```

Omitting a field preserves its existing value. Send an empty string for `expiration_date` or `password` to clear that restriction; send `disable_downloads: false` to allow downloads again. Sharing plans without link customization are forced to the 30-day expiry and cannot add download/password restrictions. Published-page controls are not individually plan-gated; the plan limits publishing itself.

### Accepting Uploads on a Shared Link[​](#accepting-uploads-on-a-shared-link "Direct link to Accepting Uploads on a Shared Link")

Board share requests also accept `enable_uploads`. It lets anyone holding the link add files to the board **without signing in** — the way to collect files you cannot fetch yourself: give the URL to a client or a teammate and ask them to drop the files on it, then poll the board for what arrives.

```
curl -X POST "https://api.playbook.com/v1/my-org/boards/main-collection/share" \

  -H "Authorization: Bearer YOUR_TOKEN" \

  -H "Content-Type: application/json" \

  -d '{ "enable_uploads": true, "password": "drop-files", "expiration_date": "2026-09-01T12:00:00Z" }'
```

**Response:**

```
{

  "data": {

    "url": "https://playbook.com/s/def456/main-collection",

    "uploads_enabled": true

  }

}
```

Three things to know before you turn it on:

* **There is no upload-only mode.** `enable_uploads` is a setting on a *share* link, so the same URL still shows the board and its assets to whoever opens it. If the recipient should not see what is already there, share a fresh, empty board instead.
* **This endpoint returns the board's existing link.** If the board was already shared for viewing, enabling uploads grants upload rights to everyone who was given that URL — it does not mint a separate one. `uploads_enabled` on the response tells you the state you just left the link in.
* **It requires a plan with shared-link customization.** Other plans get `403` rather than a silently ignored field, so a link you were told was created is never quietly read-only.

Pair it with `password` and `expiration_date` whenever the link leaves a group you trust.

### Resolving a Share Link to Its Board[​](#resolving-a-share-link-to-its-board "Direct link to Resolving a Share Link to Its Board")

If all you hold is a share URL of your own workspace — say, one a teammate pasted into a brief — turn it back into the board behind it and read that board through the regular endpoints. Do not parse the share page: its embedded data can arrive empty, and an empty board then looks exactly like a page that has not loaded.

For `https://playbook.com/s/my-org/creator-delivery`, pass the last path segment:

```
curl "https://api.playbook.com/v1/my-org/shared_links/creator-delivery" \

  -H "Authorization: Bearer YOUR_TOKEN"
```

**Response:**

```
{

  "data": {

    "url": "https://playbook.com/s/my-org/creator-delivery",

    "board_token": "main-collection",

    "board_title": "Creator delivery",

    "uploads_enabled": true,

    "expired": false

  }

}
```

Then list what is on it with `GET /v1/my-org/assets?collection_token=main-collection&nested_assets=true`.

* Only **active** links of the workspace in the path resolve. A deactivated link, a link of another workspace, or one whose board is in the trash is a `404`.
* A link made by sharing individual assets (rather than a board) has no board to resolve and returns `422` with code `unsupported_link`.
* A board's own address in the app, `https://playbook.com/my-org/{board_token}` with no `/s/`, is not a share link and needs no resolving: its last path segment already is the board token.

Over [MCP](/docs/guides/mcp.md) this is `resolve_share_link`. It takes the full URL, reads the workspace from it, and accepts a board's own address too.

***

## Publishing Boards[​](#publishing-boards "Direct link to Publishing Boards")

Publish boards as public web galleries accessible to anyone with the link.

### Creating a Published Board[​](#creating-a-published-board "Direct link to Creating a Published Board")

```
curl -X POST "https://api.playbook.com/v1/my-org/boards/portfolio/publish" \

  -H "Authorization: Bearer YOUR_TOKEN" \

  -H "Content-Type: application/json" \

  -d '{"disable_downloads": true}'
```

**Response:**

```
{

  "data": {

    "url": "https://playbook.com/p/my-org/portfolio"

  }

}
```

### Publishing with a Page Design[​](#publishing-with-a-page-design "Direct link to Publishing with a Page Design")

Send `template` to give the page a designed layout. Valid ids are the keys of `templates` in [`GET /v1/page_layout_elements`](/docs/api/page-layout-elements.md); an unknown id is refused with the list, and a page that already has a saved design answers `409 page_design_exists` instead of being reset.

```
curl -X POST "https://api.playbook.com/v1/my-org/boards/portfolio/publish" \

  -H "Authorization: Bearer YOUR_TOKEN" \

  -H "Content-Type: application/json" \

  -d '{"template": "standard"}'
```

The design can then be read and edited element by element — see [Published Page Design](/docs/guides/published_page_design.md).

Every board response also reports the page's state as `published_page`: `active`, the `template` its design uses, and the public `url` (null unless the page is live).

### Published vs. Shared[​](#published-vs-shared "Direct link to Published vs. Shared")

| Feature                 | Shared Link               | Published Link        |
| ----------------------- | ------------------------- | --------------------- |
| **Visibility**          | Private (requires link)   | Public (indexable)    |
| **Revocable**           | Yes                       | Yes (can unpublish)   |
| **Password Protection** | Yes (via UI)              | No                    |
| **Analytics**           | Detailed                  | Basic                 |
| **SEO**                 | No                        | Yes                   |
| **Best For**            | Client reviews, approvals | Portfolios, galleries |

***

## Use Cases[​](#use-cases "Direct link to Use Cases")

### 1. Client Review Process[​](#1-client-review-process "Direct link to 1. Client Review Process")

Share assets with clients for feedback:

```
async function sendForReview(assetTokens, clientEmail) {

  // Create shared links for each asset

  const sharedLinks = await Promise.all(

    assetTokens.map((token) => shareAsset(token)),

  );



  // Send email with links

  await emailService.send({

    to: clientEmail,

    subject: "Assets for Review",

    body: `

      Please review the following assets:



      ${sharedLinks.map((url, i) => `Asset ${i + 1}: ${url}`).join("\n")}



      Reply with your feedback.

    `,

  });



  return sharedLinks;

}



// Usage

await sendForReview(

  ["design-v1", "design-v2", "design-v3"],

  "client@example.com",

);
```

### 2. Portfolio Website[​](#2-portfolio-website "Direct link to 2. Portfolio Website")

Embed published boards in your website:

```
<!-- Embed published board -->

<iframe

  src="https://playbook.com/p/my-org/portfolio"

  width="100%"

  height="800px"

  frameborder="0"

  allowfullscreen

>

</iframe>
```

### 3. Temporary Access for Vendors[​](#3-temporary-access-for-vendors "Direct link to 3. Temporary Access for Vendors")

Share boards with vendors for a limited time:

```
async function grantVendorAccess(boardToken, vendorEmail, expiryDays = 7) {

  // Create shared link

  const { url } = await shareBo ard(boardToken);



  // Send with expiry notice

  await emailService.send({

    to: vendorEmail,

    subject: 'Asset Access Granted',

    body: `

      You have access to our assets for the next ${expiryDays} days:

      ${url}



      This link will be revoked after ${expiryDays} days.

    `

  });



  // Schedule revocation (implement based on your system)

  await scheduleRevocation(url, expiryDays);



  return url;

}
```

### 4. Social Media Sharing[​](#4-social-media-sharing "Direct link to 4. Social Media Sharing")

Share individual assets on social platforms:

```
async function shareToSocialMedia(assetToken, platform) {

  const shareUrl = await shareAsset(assetToken);



  const asset = await getAsset(assetToken);



  const shareText = encodeURIComponent(`Check out ${asset.title}: ${shareUrl}`);



  const socialUrls = {

    twitter: `https://twitter.com/intent/tweet?text=${shareText}`,

    facebook: `https://www.facebook.com/sharer/sharer.php?u=${shareUrl}`,

    linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${shareUrl}`,

  };



  return socialUrls[platform];

}
```

***

## Downloading Shared Assets[​](#downloading-shared-assets "Direct link to Downloading Shared Assets")

Shared links can include download capabilities. Get download URLs for shared assets:

### For Shared Assets[​](#for-shared-assets "Direct link to For Shared Assets")

```
curl "https://api.playbook.com/v1/shared/my-org/assets/product-photo/download?sharedlinkslug=abc123" \

  -H "Authorization: Bearer YOUR_TOKEN"
```

**Response:**

```
{

  "data": {

    "display_url": "https://cdn.playbook.com/product-photo.jpg",

    "raw_url": "https://cdn.playbook.com/product-photo-original.jpg",

    "size": 2456789

  }

}
```

### JavaScript Example[​](#javascript-example-1 "Direct link to JavaScript Example")

```
async function downloadSharedAsset(sharedOrgSlug, assetToken, sharedLinkSlug) {

  const response = await fetch(

    `https://api.playbook.com/v1/shared/${sharedOrgSlug}/assets/${assetToken}/download`,

    {

      headers: {

        sharedlinkslug: sharedLinkSlug,

      },

    },

  );



  const { data } = await response.json();

  return data.raw_url;

}
```

***

## Managing Shared Links[​](#managing-shared-links "Direct link to Managing Shared Links")

### Tracking Shared Link Usage[​](#tracking-shared-link-usage "Direct link to Tracking Shared Link Usage")

Implement your own tracking system:

```
class SharedLinkTracker {

  constructor() {

    this.links = new Map();

  }



  async createTrackedLink(assetOrBoardToken, type = "asset") {

    // Create the share link

    const url =

      type === "asset"

        ? await shareAsset(assetOrBoardToken)

        : await shareBoard(assetOrBoardToken);



    // Store with metadata

    this.links.set(url, {

      token: assetOrBoardToken,

      type,

      createdAt: new Date(),

      views: 0,

      lastAccessed: null,

    });



    return url;

  }



  trackAccess(url) {

    const link = this.links.get(url);

    if (link) {

      link.views++;

      link.lastAccessed = new Date();

    }

  }



  getAnalytics(url) {

    return this.links.get(url);

  }



  listActiveLinks() {

    const now = Date.now();

    const dayAgo = now - 24 * 60 * 60 * 1000;



    return Array.from(this.links.entries())

      .filter(([_, link]) => link.lastAccessed > dayAgo)

      .map(([url, link]) => ({ url, ...link }));

  }

}
```

### Revoking Access[​](#revoking-access "Direct link to Revoking Access")

While there's no direct API to revoke shared links, you can:

1. **Delete the shared asset/board**
2. **Move asset to different board**
3. **Track and warn about expired links**

```
async function revokeSharedAccess(assetToken) {

  // Option 1: Move to private board

  await updateAsset(assetToken, {

    collection_token: "private-board",

  });



  // Option 2: Archive the asset

  await updateAsset(assetToken, {

    tags: [...asset.tags, "archived"],

  });



  console.log(`Access revoked for ${assetToken}`);

}
```

***

## Best Practices[​](#best-practices "Direct link to Best Practices")

### 1. Descriptive Sharing[​](#1-descriptive-sharing "Direct link to 1. Descriptive Sharing")

Include context when sharing:

```
async function shareWithContext(assetToken, recipient, message) {

  const asset = await getAsset(assetToken);

  const shareUrl = await shareAsset(assetToken);



  await emailService.send({

    to: recipient,

    subject: `Shared Asset: ${asset.title}`,

    body: `

      ${message}



      Asset: ${asset.title}

      Link: ${shareUrl}

      Created: ${asset.created_at}

      Tags: ${asset.tags.join(", ")}



      This link will remain active until revoked.

    `,

  });

}
```

### 2. Organize Published Content[​](#2-organize-published-content "Direct link to 2. Organize Published Content")

Use consistent naming for published boards:

```
const publishedBoards = {

  portfolio: "main-portfolio",

  client_work: "client-showcase",

  team: "team-photos",

};



async function publishAll() {

  for (const [name, token] of Object.entries(publishedBoards)) {

    const { url } = await publishBoard(token);

    console.log(`Published ${name}: ${url}`);

  }

}
```

### 3. Watermark Shared Assets[​](#3-watermark-shared-assets "Direct link to 3. Watermark Shared Assets")

Add watermarks to shared assets (implementation depends on your system):

```
async function shareWithWatermark(assetToken) {

  // Get original asset

  const asset = await getAsset(assetToken);



  // Download and watermark

  const watermarked = await addWatermark(asset.display_url, {

    text: "CONFIDENTIAL",

    opacity: 0.3,

  });



  // Upload watermarked version

  const watermarkedAsset = await uploadAsset(watermarked);



  // Share the watermarked version

  return await shareAsset(watermarkedAsset.token);

}
```

### 4. Analytics and Monitoring[​](#4-analytics-and-monitoring "Direct link to 4. Analytics and Monitoring")

Track sharing activity:

```
class SharingAnalytics {

  async logShare(type, token, recipient) {

    await analytics.track({

      event: "asset_shared",

      properties: {

        type,

        token,

        recipient,

        timestamp: new Date(),

      },

    });

  }



  async getMostShared(days = 30) {

    const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);



    return await analytics.query({

      event: "asset_shared",

      since,

      groupBy: "token",

      orderBy: "count",

      limit: 10,

    });

  }

}
```

***

## Complete Sharing Workflow[​](#complete-sharing-workflow "Direct link to Complete Sharing Workflow")

Here's a complete implementation with email and tracking:

```
class PlaybookSharingManager {

  constructor(orgSlug, accessToken) {

    this.orgSlug = orgSlug;

    this.accessToken = accessToken;

    this.baseUrl = "https://api.playbook.com/v1";

  }



  async shareAsset(assetToken, options = {}) {

    const response = await fetch(

      `${this.baseUrl}/${this.orgSlug}/assets/${assetToken}/share`,

      {

        method: "POST",

        headers: { Authorization: `Bearer ${this.accessToken}` },

      },

    );



    const { data } = await response.json();



    // Track the share

    if (options.trackingId) {

      await this.logShare("asset", assetToken, options.trackingId);

    }



    return data.url;

  }



  async shareBoard(boardToken, options = {}) {

    const response = await fetch(

      `${this.baseUrl}/${this.orgSlug}/boards/${boardToken}/share`,

      {

        method: "POST",

        headers: { Authorization: `Bearer ${this.accessToken}` },

      },

    );



    const { data } = await response.json();



    if (options.trackingId) {

      await this.logShare("board", boardToken, options.trackingId);

    }



    return data.url;

  }



  async publishBoard(boardToken) {

    const response = await fetch(

      `${this.baseUrl}/${this.orgSlug}/boards/${boardToken}/publish`,

      {

        method: "POST",

        headers: { Authorization: `Bearer ${this.accessToken}` },

      },

    );



    return await response.json();

  }



  async shareWithEmail(type, token, recipients, message) {

    // Create share link

    const url =

      type === "asset"

        ? await this.shareAsset(token, { trackingId: recipients.join(",") })

        : await this.shareBoard(token, { trackingId: recipients.join(",") });



    // Get asset/board info

    const info = await this.getInfo(type, token);



    // Send emails

    for (const recipient of recipients) {

      await this.sendShareEmail(recipient, url, info, message);

    }



    return url;

  }



  async getInfo(type, token) {

    const endpoint = type === "asset" ? "assets" : "boards";

    const response = await fetch(

      `${this.baseUrl}/${this.orgSlug}/${endpoint}/${token}`,

      { headers: { Authorization: `Bearer ${this.accessToken}` } },

    );



    const { data } = await response.json();

    return data;

  }



  async sendShareEmail(recipient, url, info, message) {

    // Implement email sending

    console.log(`Sending to ${recipient}:`, {

      url,

      title: info.title,

      message,

    });

  }



  async logShare(type, token, trackingId) {

    // Log to your analytics system

    console.log("Share logged:", { type, token, trackingId });

  }

}



// Usage

const manager = new PlaybookSharingManager("my-org", "your_token");



// Share asset with email

await manager.shareWithEmail(

  "asset",

  "product-photo",

  ["client@example.com", "stakeholder@example.com"],

  "Please review this product photo and provide feedback.",

);



// Publish board

const published = await manager.publishBoard("portfolio");

console.log("Portfolio published at:", published.data.url);
```

***

## Related API Endpoints[​](#related-api-endpoints "Direct link to Related API Endpoints")

* [Share Asset](/docs/api/share-organization-asset.md)
* [Share Board](/docs/api/share-organization-collection.md)
* [Resolve Share Link](/docs/api/resolve-organization-shared-link.md)
* [Publish Board](/docs/api/publish-organization-collection.md)
* [Download Shared Asset](/docs/api/download-shared-asset.md)

***

## Next Steps[​](#next-steps "Direct link to Next Steps")

* Learn about [Asset Management](/docs/guides/asset_management.md) to organize shared content
* Explore [Webhooks](/docs/guides/webhooks.md) to track sharing events
* Read about [Comments](/docs/guides/comments.md) for collaboration on shared assets
