Home - ajsb85/penpot-api-client GitHub Wiki

This document outlines a professional-grade TypeScript client for the Penpot API. The library is designed to provide a robust, maintainable, and ergonomic interface for developers interacting with a Penpot instance. It is environment-agnostic, leveraging native platform APIs to ensure compatibility with Node.js, Deno, and modern browser environments without third-party runtime dependencies.

Features

Library Architecture & Design

  • Fluent API Design: The client employs a fluent, instantiated interface. After initializing the main PenpotClient once, all API command modules are accessed as properties on the client instance (e.g., client.teams.list(), client.files.get()). This design promotes cleaner, more readable code and eliminates the need to pass the client instance to every function call.
  • Unified Real-time Client: A single RealtimeClient offers support for both WebSocket (/api/ws) and Server-Sent Events (/api/sse) protocols. This allows developers to choose the optimal transport for their environment while using a consistent, event-driven API (realtimeClient.on(...)) for live data synchronization.
  • Strict Type Safety: Developed entirely in TypeScript, the library ensures strong type safety for all API requests, responses, and real-time event payloads. A comprehensive RealtimeEventMap links event names to their specific data interfaces, enabling editor autocompletion and preventing type-related runtime errors.
  • Structured Error Handling: The client throws custom, typed errors to distinguish between different failure modes. Developers can programmatically handle specific conditions by catching ApiHttpError for network status issues or ApiRpcError for API-level rejections.
  • Semantic Constants: To enhance code clarity and prevent common errors, the library exports constant objects for known API values, such as RealtimeEvents, ApiErrorCodes, and ObjectTypes.

Backend Technology Stack

The Penpot backend, which this library communicates with, is built on a robust and scalable technology stack:

  • Primary Language: Clojure, a dynamic, functional dialect of Lisp that runs on the Java Virtual Machine (JVM).
  • Database: PostgreSQL is used for primary data persistence.
  • Real-time Communication: The backend manages real-time collaboration through a publish-subscribe (pub/sub) message bus, exposing events to clients via WebSockets and Server-Sent Events.
  • API Format: The API uses a custom RPC (Remote Procedure Call) system over HTTP, with payloads serialized in a Clojure-compatible, keyword-based JSON format (Transit-JSON).

API Command Coverage

The library provides comprehensive coverage of the Penpot RPC API. The following table summarizes the available commands, grouped by module.

Module Method Description
auth login() Authenticates a user with email and password.
loginWithProvider() Authenticates a user via an external provider (LDAP, SAML, etc.).
register() Registers a new user on the instance.
users get() Retrieves the profile of the currently authenticated user.
getById() Retrieves the public profile of a specific user by their ID.
update() Updates the current user's profile information.
updatePhoto() Uploads and sets a new profile photo for a user.
teams get() Retrieves the details of a specific team by its ID.
list() Retrieves all teams the current user is a member of.
create() Creates a new team.
update() Updates the name of an existing team.
delete() Deletes a team.
listMembers() Lists all members of a given team.
projects get() Retrieves a specific project (folder) by its ID.
list() Retrieves all projects for a given team.
create() Creates a new project (folder).
delete() Deletes a project and all its files.
files get() Retrieves the metadata for a specific file.
create() Creates a new file in a given team/project.
update() Updates a file's metadata, such as its name.
delete() Deletes a file.
duplicate() Creates a copy of an existing file.
fileContent getPage() Retrieves the full data for a specific page within a file.
listSnapshots() Lists all named versions (snapshots) of a file.
createSnapshot() Creates a new named version (snapshot) of a file.
import() Imports a .penpot binary file.
assets upload() Uploads a media asset (e.g., an image) to a specific file.
createFromUrl() Imports a media asset into a file from a public URL.
comments listThreads() Retrieves all comment threads for a given file.
create() Adds a new comment or creates a new thread.
update() Edits the text of an existing comment.
webhooks list() Retrieves all webhooks configured for a team.
create() Creates a new webhook for a team.
update() Updates an existing webhook.
delete() Deletes a webhook.

Real-time Events

The RealtimeClient allows you to subscribe to the following server-side events. Use the exported RealtimeEvents constant for type-safe subscriptions.

Constant Event Name Description
RealtimeEvents.FileChange :file-change Fired when any data within a file is modified.
RealtimeEvents.CommentAdded :comment-added Triggered when a new comment is added to a thread.
RealtimeEvents.CommentUpdated :comment-updated Fired when the text content of an existing comment is edited.
RealtimeEvents.CommentDeleted :comment-deleted Fired when a comment is deleted.
RealtimeEvents.CommentThreadUpdated :comment-thread-updated Triggered when a thread's metadata (status, position) changes.
RealtimeEvents.UserNotification :user-notification A generic event for user-specific notifications (e.g., mentions).
RealtimeEvents.FileLibraryUpdated :file-library-updated Fired when a shared library used by the current file has been updated.
RealtimeEvents.UserJoinedFile :user-joined-file Triggered when a new user joins a real-time file session.
RealtimeEvents.UserLeftFile :user-left-file Triggered when a user leaves a real-time file session.
RealtimeEvents.UserActivity :user-activity Broadcasts a user's cursor position and selection to other collaborators.
RealtimeEvents.TeamUpdated :team-updated Fired when a team's details (like its name) are changed.
RealtimeEvents.TeamMemberAdded :team-member-added Triggered when a new member is added to a team you are part of.
RealtimeEvents.TeamMemberRemoved :team-member-removed Triggered when a member is removed from a team you are part of.

Installation and Usage

To install the library, use a standard package manager:

npm install penpot-api-client

Before use, a Personal Access Token must be generated from your Penpot instance.

Client Initialization

import { PenpotClient, ApiRpcError, ApiErrorCodes } from 'penpot-api-client';

const client = new PenpotClient({
  baseUrl: '[https://design.penpot.app](https://design.penpot.app)', // Your instance URL
  accessToken: 'YOUR_PERSONAL_ACCESS_TOKEN',
});

async function getUserProfile() {
  try {
    const profile = await client.users.get();
    console.log(`Successfully authenticated as: ${profile.fullName}`);
  } catch (error) {
    if (error instanceof ApiRpcError && error.code === ApiErrorCodes.Unauthorized) {
      console.error('Authentication failed. Please verify your access token.');
    } else {
      console.error('An unexpected error occurred:', error);
    }
  }
}

getUserProfile();