Uploads
Two operations take a file instead of JSON: replacing an article's hero image, and adding the bytes of a knowledge source. For both, body is the file and contentType is required.
body is a Blob, an ArrayBuffer or a Uint8Array. A File is a Blob, and a Node Buffer is a Uint8Array, so both work. contentType is sent as the Content-Type header and tells the API how to read the bytes; the TypeScript type lists the values the operation accepts. Any other content type is refused by the API with 415.
An upload is a PUT, so it is never retried (see Retries and timeouts). The timer of timeoutMs runs while the file is sent; pass a larger init.timeoutMs for a big file.
Hero image
pd.articles.setHeroFromFile replaces the article's cover. It needs the images.update permission.
const image = await fetch("https://example.com/lamp.png");
const { asset } = await pd.articles.setHeroFromFile({
articleId,
altText: "A lamp on a desk",
body: new Uint8Array(await image.arrayBuffer()),
contentType: "image/png",
});
console.log(asset.publicUrl);
// In Node or Bun, the bytes of a file on disk work the same way:
const body = await readFile("lamp.png"); // a Buffer, which is a Uint8Array
- Types:
image/png,image/jpeg,image/webp,image/gifandimage/avif. - Size: 25 MB at most; above that the API answers
413. - Bytes: they must decode as an image, or the API answers
400. The content type alone is not trusted. altTextis optional, 1 to 300 characters. Left out, the previous hero's alt text is kept, or the article's title if there was none.- The image is stored whole in the hero box and never cropped.
- One hero per article. The new image replaces the old. If another replacement landed at the same moment the API answers
409(CONFLICT); try again. - Quota. The new image must fit the workspace's storage quota, or the API answers
429(QUOTA_EXCEEDED). - Webhook.
image.regeneratedis sent withtype: "hero", and a published article also sendsarticle.updated. See Webhooks.
To use a picture that is already on the web, let the API fetch it with pd.articles.setHeroFromUrl({ articleId, body: { imageUrl } }). It needs the same permission, sends the same webhook, takes 25 MB and 15 seconds at most, and refuses private network addresses.
Knowledge source file
A knowledge source holds material the AI can draw on. Adding a file is two calls: register the source, then upload its bytes. Both need the knowledge.create permission.
const { source } = await pd.knowledge.create({
workspaceId,
projectId,
body: { type: "markdown", name: "Style notes" },
});
await pd.knowledge.uploadContent({
workspaceId,
sourceId: source.id,
body: new Blob(["# Style notes\n\nShort sentences.\n"]),
contentType: "text/markdown",
});
The source's type must match the file you upload:
Source type |
contentType |
|---|---|
pdf |
application/pdf |
docx |
application/vnd.openxmlformats-officedocument.wordprocessingml.document |
txt |
text/plain |
markdown |
text/markdown |
- Size: 10 MiB at most; above that the API answers
413. - Mismatch: a file whose type differs from the source's answers
400(INVALID_REQUEST). - Empty body: answers
400. - Unknown source:
404(RESOURCE_NOT_FOUND). - Ingestion runs in the background. The call returns the source; read
statusonpd.knowledge.listto follow it.
To run ingestion again, call pd.knowledge.reingest.
Images in an imported article
The body of an article is not uploaded. To import Markdown with images, send contentMarkdown to pd.articles.create: the API downloads each image the Markdown refers to. See Recipes.