Skip to main content

Sharing and Publishing

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

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

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

Sharing Assets

Create temporary shared links for individual assets.

curl -X POST "https://api.playbook.com/v1/my-org/assets/product-photo/share?access_token=TOKEN"

Response:

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

JavaScript Example

async function shareAsset(assetToken) {
const response = await fetch(
`https://api.playbook.com/v1/${ORG_SLUG}/assets/${assetToken}/share?access_token=${ACCESS_TOKEN}`,
{ method: 'POST' }
);

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

// Usage
const shareUrl = await shareAsset('product-photo');
console.log('Share link:', shareUrl);

Sharing Boards

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

curl -X POST "https://api.playbook.com/v1/my-org/boards/main-collection/share?access_token=TOKEN"

Response:

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

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)

Publishing Boards

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

Creating a Published Board

curl -X POST "https://api.playbook.com/v1/my-org/boards/portfolio/publish?access_token=TOKEN"

Response:

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

Published vs. Shared

FeatureShared LinkPublished Link
VisibilityPrivate (requires link)Public (indexable)
RevocableYesYes (can unpublish)
Password ProtectionYes (via UI)No
AnalyticsDetailedBasic
SEONoYes
Best ForClient reviews, approvalsPortfolios, galleries

Use Cases

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'],
'[email protected]'
);

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

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

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

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

For Shared Assets

curl "https://api.playbook.com/v1/shared/my-org/assets/product-photo/download?sharedlinkslug=abc123&access_token=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

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;
}

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

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

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

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

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

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

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?access_token=${this.accessToken}`,
{ method: 'POST' }
);

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?access_token=${this.accessToken}`,
{ method: 'POST' }
);

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?access_token=${this.accessToken}`,
{ method: 'POST' }
);

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}?access_token=${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',
['[email protected]', '[email protected]'],
'Please review this product photo and provide feedback.'
);

// Publish board
const published = await manager.publishBoard('portfolio');
console.log('Portfolio published at:', published.data.url);


Next Steps