Skip to content

Project Management

API Type

PWA Backend API - Uses access keys in X-Albumstory header. See Authentication.

Manage user projects: list, delete, duplicate, and transfer ownership.

Environment URLs:

  • Development: https://pwa-api-dev.photobook.ai/v2/*
  • Production: https://pwa-api.photobook.ai/v2/*

List User Projects

Retrieve all projects for a specific user.

Endpoint

GET https://pwa-api{-env}.photobook.ai/v2/project/list?user={userId}

Authentication

See Authentication - PWA Backend

Query Parameters

ParameterTypeRequiredDescription
userstringYesEnd user's identifier (userId or email used during creation)
cursorstringNoPagination token from previous response. Fetches next page of results

Response

json
{
  "data": [
    {
      "title": "Summer Memories 2024",
      "preview": "https://cdn.photobook.ai/previews/abc123.jpg",
      "id": "p-abc123xyz",
      "status": {
        "photos": "idle",
        "project": "editable"
      },
      "createdAt": 1713190200000,
      "updatedAt": 1713276600000,
      "expiresAt": 1715868600,
      "product": {
        "psp": "PrinterName",
        "sku": "PB001",
        "marketId": 0,
        "marketIdAlt": "custom-market"
      },
      "link": "https://{frontend}.photobook.ai?id=p-abc123xyz",
      "ordered": false
    }
  ],
  "cursor": "next-pagination-token"
}

Response Fields

FieldTypeDescription
dataarrayList of user's projects
cursorstringA pagination cursor. If defined, suggests that there is another page to fetch

Project Object

FieldTypeDescription
titlestringProject title (inferred from cover text, falls back to SKU)
previewstringPreview image URL (empty if not saved yet)
idstringProject ID (starts with p-)
statusobjectProject and photo status
status.photosstringPhoto status: none, uploading, analyzing, idle
status.projectstringProject status: created, building, editable, deleted
createdAtintegerUnix timestamp in milliseconds
updatedAtintegerUnix timestamp in milliseconds
expiresAtintegerUnix timestamp in seconds (when project will be deleted)
productobjectProduct details
product.pspstringPrinter name
product.skustringProduct SKU
product.marketIdintegerInternal market ID
product.marketIdAltstringAlternative market identifier
linkstringDirect link to project in PWA editor
orderedbooleanWhether project has been ordered

Pagination

The API returns a maximum of 100 orders per request. Use the cursor field to fetch additional pages.

How Pagination Works

  1. Make initial request without cursor
  2. Display the 100 results
  3. If response includes a cursor, there are more results
  4. Make next request with the cursor value
  5. Repeat until cursor is undefined

⚠️ Important

  • data may be empty even if cursor is returned
  • Always check for cursor to determine if more pages exist
  • Continue fetching as long as cursor is present

Example

javascript
async function getUserProjects(userId) {
  const response = await fetch(
    `https://pwa-api-dev.photobook.ai/v2/project/list?user=${encodeURIComponent(userId)}${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ''}`,
    {
      method: 'GET',
      headers: {
        'X-Albumstory': JSON.stringify({
          accessKey: process.env.PWA_ACCESS_KEY,
          accessSecret: process.env.PWA_ACCESS_SECRET
        })
      }
    }
  );
  
  const data = await response.json();
  return data.data;
}

Get Project Status

Retrieve the current status of a specific project.

Endpoint

GET https://pwa-api{-env}.photobook.ai/v2/project/status?id={projectId}

Authentication

See Authentication - PWA Backend

Query Parameters

ParameterTypeRequiredDescription
idstringYesProject ID (starts with p-)

Response

json
{
  "id": "p-abc123xyz",
  "status": {
    "photos": "idle",
    "project": "editable",
    "order": "delivering"
  },
  "user": {
    "firstName": "John",
    "lastName": "Doe",
    "phone": "+1512345678",
    "shippingAddress": {
      "country": "United States",
      "city": "Jacksonville",
      "isoCode2": "US",
      "isoCode3": "USA",
      "state": "Florida",
      "postal": "12345",
      "line1": "123 Florida Way"
    },
    "userId": "johndoe",
    "email": "random@photobook.ai",
    "actualEmail": "johndoe@johndoe.net"
  },
  "tracking": "https://www.ups.com/track?loc=en_US&requester=QUIC%2F/trackdetails&tracknum=1ABC1234"
}

Response Fields

FieldTypeDescription
idstringProject ID
statusobjectProject and photo status
status.photosstringPhoto status: none, uploading, analyzing, idle
status.projectstringProject status: created, building, editable, deleted
status.orderstring | undefinedFulfillment status (e.g. delivering); only present after the project has been sent for printing
userobject | undefinedEnd user details; only present after the project has been sent for printing
user.userIdstringUser's internal identifier
user.emailstring | undefinedUser's email address
user.firstNamestring | undefinedUser's first name
user.lastNamestring | undefinedUser's last name
user.phonestring | undefinedUser's phone number
user.shippingAddressobjectUser's shipping address
user.shippingAddress.line1stringAddress line 1
user.shippingAddress.citystringCity
user.shippingAddress.statestringState or region
user.shippingAddress.postalstringPostal / ZIP code
user.shippingAddress.countrystringCountry name
user.shippingAddress.isoCode2string2-letter ISO country code
user.shippingAddress.isoCode3string3-letter ISO country code
trackingstring | undefinedCarrier tracking URL; only present when status.order is delivering, and may not always be available

Status Meanings

Photo Status

ValueDescription
noneNo photos uploaded
uploadingPhotos currently uploading
analyzingPhotos being analyzed
idlePhotos ready (not uploading or analyzing)

Project Status

ValueDescription
createdProject created, book data not yet generated
buildingBook data being generated
editableReady for user editing
orderingProject has been submitted for ordering
expiredProject has expired and is no longer accessible
deletedMarked for deletion (data expunged after 2 days)

Order Status

INFO

Statuses after transferring to GPS (PBAI's Global Print System) for fulfillment with PSPs/Printers

ValueDescription
waitingOrder item created; assets and payment not yet made
expiringOrder item created but still waiting; will expire soon
last callOrder item in expiring state; last chance to complete
files missingPayment made but assets not uploaded after timeout
receivedAssets uploaded
paidPayment received
processingPayment and assets complete; being processed
renderingRendering final assets
renderedFinal assets rendered
sending printjobSending print job to PSP
sent printjobPrint job sent to PSP
acceptedPSP received and accepted the order
printingPSP is printing the order
printedPSP has printed the order
deliveringOrder is now being delivered (tracking URL may be available)
completeOrder fully completed
ready for collectionReady for collection / pickup
collectedOrder collected
abnormalAbnormal delivery reported by PSP
failed processingFailed processing reported by PSP
on holdOrder placed on hold by PSP
undeliveredCourier was unable to deliver the package
expiredCourier stopped tracking; no new updates received
cleanedOrder assets removed due to age
cancelledOrder cancelled by eCommerce
refundedOrder refunded by eCommerce
abandonedPaid but assets not uploaded after final timeout, or passed last call
deletedOrder manually deleted

Example

javascript
async function getProjectStatus(projectId) {
  const response = await fetch(
    `https://pwa-api-dev.photobook.ai/v2/project/status?id=${encodeURIComponent(projectId)}`,
    {
      method: 'GET',
      headers: {
        'X-Albumstory': JSON.stringify({
          accessKey: process.env.PWA_ACCESS_KEY,
          accessSecret: process.env.PWA_ACCESS_SECRET
        })
      }
    }
  );

  return await response.json();
}

Delete Projects

Delete one or more projects for a user.

Endpoint

POST https://pwa-api{-env}.photobook.ai/v2/project/delete

Request Parameters

json
{
  "user": "user-12345",
  "type": "async",
  "ids": [
    "p-abc123xyz",
    "p-def456uvw"
  ]
}
ParameterTypeRequiredDescription
userstringYesEnd user's identifier
typestringYesDeletion mode: async or sync
idsarrayYesProject IDs to delete (max 10 per request)

Deletion Types

  • async - Initiates deletion and returns immediately (recommended)
  • sync - Waits for deletion to complete before responding

Max Projects

Maximum of 10 project IDs per API call.

Response

json
{
  "message": "scheduled to delete p-abc123xyz, p-def456uvw"
}

Example

javascript
async function deleteProjects(userId, projectIds) {
  const response = await fetch('https://pwa-api-dev.photobook.ai/v2/project/delete', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Albumstory': JSON.stringify({
        accessKey: process.env.PWA_ACCESS_KEY,
        accessSecret: process.env.PWA_ACCESS_SECRET
      })
    },
    body: JSON.stringify({
      user: userId,
      type: 'async',
      ids: projectIds.slice(0, 10) // Ensure max 10
    })
  });
  
  return await response.json();
}

Duplicate Project

Create a copy of an existing project for the same user.

Endpoint

POST https://pwa-api{-env}.photobook.ai/v2/project/duplicate

Request Parameters

json
{
  "id": "p-abc123xyz",
  "callback": "https://your-app.com/api/order-callback"
}
ParameterTypeRequiredDescription
idstringYesProject ID to duplicate
callbackstringNoOverride callback URL for the duplicated project

Response

json
{
  "title": "Summer Memories 2024",
  "preview": "https://cdn.photobook.ai/previews/new123.jpg",
  "id": "p-new123xyz",
  "endUserId": "user-12345",
  "createdAt": 1713190200000,
  "updatedAt": 1713190200000,
  "expiresAt": 1715868600,
  "product": {
    "sku": "PB001",
    "psp": "PrinterName",
    "marketId": 0,
    "marketIdAlt": "custom-market"
  },
  "link": "https://{frontend}.photobook.ai?id=p-new123xyz"
}

Example

javascript
async function duplicateProject(projectId, newCallback = null) {
  const requestData = { id: projectId };
  if (newCallback) {
    requestData.callback = newCallback;
  }
  
  const response = await fetch('https://pwa-api-dev.photobook.ai/v2/project/duplicate', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Albumstory': JSON.stringify({
        accessKey: process.env.PWA_ACCESS_KEY,
        accessSecret: process.env.PWA_ACCESS_SECRET
      })
    },
    body: JSON.stringify(requestData)
  });
  
  const data = await response.json();
  return data.id; // Return new project ID
}

Transfer Project

Transfer project ownership to another user.

Endpoint

POST https://pwa-api{-env}.photobook.ai/v2/project/transfer

Request Parameters

json
{
  "id": "p-abc123xyz",
  "targetUser": "new-user-12345",
  "callback": "https://your-app.com/api/order-callback"
}
ParameterTypeRequiredDescription
idstringYesProject ID to transfer
targetUserstringYesTarget user identifier to transfer ownership to
callbackstringNoOverride callback URL for the transferred project

Response

json
{
  "title": "Summer Memories 2024",
  "preview": "https://cdn.photobook.ai/previews/abc123.jpg",
  "id": "p-abc123xyz",
  "endUserId": "new-user-12345",
  "createdAt": 1713190200000,
  "updatedAt": 1713276600000,
  "expiresAt": 1715868600,
  "product": {
    "sku": "PB001",
    "psp": "PrinterName",
    "marketId": 0,
    "marketIdAlt": "custom-market"
  },
  "link": "https://{frontend}.photobook.ai?id=p-abc123xyz"
}

⚠️

After transfer, the endUserId reflects the new owner. The original user will no longer see this project in their list.

Example

javascript
async function transferProject(projectId, newUserId) {
  const response = await fetch('https://pwa-api-dev.photobook.ai/v2/project/transfer', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Albumstory': JSON.stringify({
        accessKey: process.env.PWA_ACCESS_KEY,
        accessSecret: process.env.PWA_ACCESS_SECRET
      })
    },
    body: JSON.stringify({
      id: projectId,
      targetUser: newUserId
    })
  });
  
  return await response.json();
}

Use Cases

Display User's Projects

javascript
async function displayProjects(userId) {
  const projects = await getUserProjects(userId);
  
  // Filter by status
  const editableProjects = projects.filter(p => 
    p.status.project === 'editable' && !p.ordered
  );
  
  // Sort by recently updated
  editableProjects.sort((a, b) => b.updatedAt - a.updatedAt);
  
  return editableProjects;
}

Cleanup Old Projects

javascript
async function cleanupExpiredProjects(userId) {
  const projects = await getUserProjects(userId);
  const now = Date.now() / 1000; // Convert to seconds
  
  // Find projects expiring soon (within 7 days)
  const expiringSoon = projects.filter(p => 
    p.expiresAt - now < 7 * 24 * 60 * 60
  );
  
  if (expiringSoon.length > 0) {
    console.log(`${expiringSoon.length} projects expiring soon`);
  }
}

Batch Delete

javascript
async function batchDeleteProjects(userId, projectIds) {
  // Delete in batches of 10
  const batches = [];
  for (let i = 0; i < projectIds.length; i += 10) {
    batches.push(projectIds.slice(i, i + 10));
  }
  
  for (const batch of batches) {
    await deleteProjects(userId, batch);
  }
}

Suggested Practices

  1. Check Status - Use status.project to determine if project is ready for editing
  2. Handle Expiration - Display expiration warnings to users with upcoming expiresAt dates
  3. Async Deletion - Use async deletion type for better performance
  4. Batch Wisely - When deleting multiple projects, respect the 10-project limit

photobook.ai Developer Documentation