Getting started
PlentyLabs' public API is built using GraphQL. It is the same API our own apps use. If you are new to GraphQL, Apollo has resources for beginners. The official GraphQL documentation is another good starting point.
Endpoint
PlentyLabs' GraphQL endpoint is:
https://api.plentylabs.com/graphqlThe published schema.graphql describes everything an API key can use, and operations.graphql holds every published operation document, so you can browse the schema in your editor.
Authentication
The PlentyLabs API supports workspace API keys.
API keys
API keys are the way to access the API from your own code. Visit Settings, Developers, API keys in PlentyLabs to create and manage them. Only a workspace owner can create a key, and a key works only in its own workspace.
To authenticate your requests, pass the key as a bearer token with the header Authorization: Bearer <API_KEY>
curl https://api.plentylabs.com/graphql \
-H "Authorization: Bearer $PLENTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "query": "query GetWorkspace { apiCredentialWorkspace { id name } }" }'See Authentication for access levels, expiry and revocation.
Error handling
PlentyLabs' GraphQL API follows the standard GraphQL error format, returning errors within an errors array in the response. Each error object includes a message describing what went wrong, a path array indicating where in the query the error occurred, and extensions with a code you can branch on.
Always check for the errors array before assuming success. GraphQL queries can partially succeed with a 200 HTTP status, returning some data while including errors for failed fields. Monitor HTTP status codes for server errors (5xx) and make sure to correctly handle rate limits. Use strongly typed GraphQL clients when possible to catch validation errors before a request is sent, and always validate required fields to avoid runtime null value errors.
{
"data": { "board": null },
"errors": [
{
"message": "Board not found",
"path": ["board"],
"extensions": { "code": "NOT_FOUND" }
}
]
}Mutations can also return errors as data. Every mutation payload has an errors list of typed MutationError objects, such as a RateLimitedError with retryAfterSeconds. Select it when you want to handle a failure without parsing the top-level errors.
These are the error codes the API returns in extensions.code:
| Code | Meaning |
|---|---|
BAD_REQUEST | The request body is not a valid GraphQL request. |
BAD_USER_INPUT | An argument was refused. |
CONFLICT | The change conflicts with the object's current state. |
CREDIT_LIMIT_REACHED | The key's creator reached the credit limit a workspace owner set for them. |
EXTERNAL_SERVICE_ERROR | A service the operation depends on failed. |
FORBIDDEN | The key cannot select a field, or cannot reach the object. |
GRAPHQL_VALIDATION_FAILED | The document does not match the schema. |
INSUFFICIENT_CREDITS | The workspace does not have enough credits. |
INTERNAL_SERVER_ERROR | Something failed on our side. |
MFA_ENROLLMENT_REQUIRED | The workspace requires two-factor authentication and the key's creator has not set it up. |
NOT_FOUND | The object does not exist. |
PAYLOAD_TOO_LARGE | The request body is larger than 25 MB. |
QUERY_TOO_COMPLEX | The document is too deep or selects too many fields. See rate limiting. |
RATE_LIMITED | Too many requests. See rate limiting. |
REQUEST_TIMEOUT | The request ran longer than its time limit. |
TIMEOUT | A step inside the operation timed out. |
UNAUTHENTICATED | The key is missing, expired or revoked. |
Getting started
We recommend using a GraphQL client to explore the schema. Our API is explorable and queryable in the Playground, signed in to your workspace: pick an operation to see its document and variables, and run it against your own data. No download required.
Once you have your client set up, you can start making queries (read) and mutations (write) to the API.
Queries & mutations
To get information about the workspace your key belongs to, you can use the apiCredentialWorkspace query:
query GetWorkspace {
apiCredentialWorkspace {
id
name
}
}As boards (and most other objects) are workspace based, every list query takes the workspaceId from that result:
query ListBoards($workspaceId: ID!) {
boards(workspaceId: $workspaceId, first: 10) {
edges {
node {
id
title
assetCount
}
}
}
}Once you have found the right board, you can get its pages and the cards on each page. Cards live on pages, so card operations take a page id, not the board id. Let's make a request with some other card metadata:
query GetBoard($id: ID!) {
board(id: $id) {
id
title
pages(first: 10) {
edges {
node {
id
title
cards(first: 50) {
edges {
node {
id
assetType
assetId
positionX
positionY
width
height
}
}
}
}
}
}
}
}We can also get any object by id with the node query, whichever type it is:
query GetNode($id: ID!) {
node(id: $id) {
__typename
id
... on Board {
title
}
... on Image {
status
downloadUrl
}
}
}Creating boards & generating images
To create a new board, use a mutation:
mutation CreateBoard($workspaceId: ID!) {
createBoard(input: { workspaceId: $workspaceId, title: "Spring campaign" }) {
success
board {
id
title
pages(first: 1) {
edges {
node {
id
}
}
}
}
errors {
message
}
}
}This mutation will create a new board and return its id and title if the call was successful (success: true). A new board comes with one page; keep that page's id for placing cards.
A common use case after creating a board is generating an image onto it. To do this we use the generateImage mutation. Generation returns at once, with each image in status QUEUED; credits are charged when the mutation is accepted and refunded if the generation fails.
mutation GenerateImage($workspaceId: ID!) {
generateImage(
workspaceId: $workspaceId
input: { prompt: "A ceramic mug on a linen table", aspectRatio: "4:5", count: 1 }
) {
success
images {
id
status
}
errors {
message
}
}
}Poll asset(id) until status is COMPLETED or FAILED, then place the image on the page with createCanvasCards. Generating an asset walks through the whole flow, including video and audio.
Accessing media
url, videoUrl and thumbnailUrl load only inside the PlentyLabs app. To show or fetch media anywhere else, use the signed fields. signedUrl on images and audio, signedVideoUrl and signedThumbnailUrl on videos, and signedThumbnailUrl on artboards load from any origin. downloadUrl and thumbnailDownloadUrl download the file. Each link is valid for one hour; select the field again for a fresh one.
signedUrl on images and signedThumbnailUrl take max, the longest side in pixels up to 4096, and format to return a resized copy.
Other examples
Queries
There are many ways to fetch assets. One common use case is to get the videos on one board:
query BoardVideos($workspaceId: ID!, $boardId: ID!) {
assets(workspaceId: $workspaceId, first: 20, filter: { types: [VIDEO], boardIds: [$boardId] }) {
edges {
node {
id
displayName
... on Video {
status
duration
downloadUrl
}
}
}
}
}Searching the library works the same way, with the search argument:
query SearchAssets($workspaceId: ID!) {
assets(workspaceId: $workspaceId, first: 20, search: "mug") {
edges {
node {
__typename
id
displayName
}
}
}
}Processing assets
Assets that are still being generated are hidden by default from assets. They can be included by passing includeProcessing: true in the filter.
Support
If you run into problems or have questions or suggestions, send us a note at support@plentylabs.com.