API Documentation
Programmatically manage your boards and pins via HTTPS. JSON in, JSON out. API access is part of the Enterprise plan.
Authentication
Generate an API key from Settings → API keys. Send it with every request as a Bearer token:
Authorization: Bearer ptp_live_xxxxxxxxxxxxxxxxxxxxxxxxEach key has a set of scopes that limit what it can do:
boards:read— list your boardsboards:write— create / update boardspins:read— read pins on your boardspins:write— create / update / delete pins
Base URL
https://corkboard.social/api/public/v1Boards
/api/public/v1/boardscurl -H "Authorization: Bearer ptp_live_..." \
https://corkboard.social/api/public/v1/boards?limit=25{
"boards": [
{
"id": "uuid", "title": "...", "slug": "...",
"description": "...", "visibility": "public",
"pins_count": 12, "links_count": 4,
"views_count": 320,
"created_at": "...", "updated_at": "..."
}
]
}/api/public/v1/boardsRequest body:
{
"title": "My board", // string, 1–120, required
"description": "Optional", // string, ≤500, optional
"visibility": "public" // "public" | "unlisted" | "private", default "public"
}curl -X POST \
-H "Authorization: Bearer ptp_live_..." \
-H "Content-Type: application/json" \
-d '{"title":"My board","visibility":"public"}' \
https://corkboard.social/api/public/v1/boardsResponse (201):
{
"board": {
"id": "uuid",
"title": "My board",
"slug": "my-board",
"description": null,
"visibility": "public",
"created_at": "2026-06-21T10:00:00.000Z"
}
}Pins
/api/public/v1/pins?board_id=<uuid>curl -H "Authorization: Bearer ptp_live_..." \
"https://corkboard.social/api/public/v1/pins?board_id=BOARD_UUID&limit=50"Response (200):
{
"pins": [
{
"id": "uuid",
"board_id": "uuid",
"slug": "pin-lxyz",
"name": "My pin",
"photo_url": "https://...",
"short_desc": "Short text",
"badges": ["new"],
"pos_x": 120, "pos_y": 80, "rotation": 0,
"pin_variant": "pin-01",
"style": null,
"likes_count": 0,
"comments_count": 0,
"created_at": "2026-06-21T10:00:00.000Z",
"updated_at": "2026-06-21T10:00:00.000Z"
}
]
}/api/public/v1/pinsRequest body:
{
"board_id": "BOARD_UUID", // required
"name": "My pin", // required, 1–120 chars
"slug": "my-pin", // optional; auto-generated if omitted
"photo_url": "https://...", // optional, string | null
"short_desc": "Description", // optional, ≤500 chars
"badges": ["new","hot"], // optional, up to 12 strings
"pos_x": 120, // optional, number (default 0)
"pos_y": 80, // optional, number (default 0)
"rotation": 0, // optional, number (default 0)
"pin_variant":"pin-01", // optional, one of "pin-01" … "pin-10" (see Pin variants)
"style": { /* Pin style object — see Styles & widgets below */ }
}curl -X POST \
-H "Authorization: Bearer ptp_live_..." \
-H "Content-Type: application/json" \
-d '{
"board_id":"BOARD_UUID",
"name":"My pin",
"photo_url":"https://example.com/p.jpg",
"badges":["new","featured"],
"pos_x":120, "pos_y":80
}' \
https://corkboard.social/api/public/v1/pinsResponse (201):
{
"pin": {
"id": "uuid",
"board_id": "uuid",
"slug": "my-pin",
"name": "My pin",
"photo_url": "https://example.com/p.jpg",
"short_desc": null,
"badges": ["new","featured"],
"pos_x": 120, "pos_y": 80, "rotation": 0,
"pin_variant": "pin-01",
"style": null,
"likes_count": 0,
"comments_count": 0,
"created_at": "2026-06-21T10:00:00.000Z",
"updated_at": "2026-06-21T10:00:00.000Z"
}
}/api/public/v1/pins/:pinIdcurl -H "Authorization: Bearer ptp_live_..." \
https://corkboard.social/api/public/v1/pins/PIN_UUIDResponse (200): { pin: Pin } — see schema below.
/api/public/v1/pins/:pinIdRequest body — any subset of:
{
"name": "Renamed", // 1–120 chars
"photo_url": "https://...", // string | null (send null to clear)
"short_desc": "New desc", // string | null, ≤500 chars
"badges": ["sale"], // replaces the array, up to 12
"pos_x": 300, // number
"pos_y": 120, // number
"rotation": 15, // number (degrees)
"pin_variant": "pin-03", // "pin-01" … "pin-10"
"style": { /* Pin style object, see below */ } // or null to clear
}curl -X PATCH \
-H "Authorization: Bearer ptp_live_..." \
-H "Content-Type: application/json" \
-d '{"name":"Renamed","pos_x":300,"pos_y":120}' \
https://corkboard.social/api/public/v1/pins/PIN_UUIDResponse (200): { pin: Pin } — the full updated pin.
/api/public/v1/pins/:pinIdcurl -X DELETE \
-H "Authorization: Bearer ptp_live_..." \
https://corkboard.social/api/public/v1/pins/PIN_UUIDResponse (200):
{ "ok": true, "deleted": "PIN_UUID" }What the API can not do
To keep boards fair, the public API is intentionally scoped to content you own. The following are not exposed via API — and we won't add them:
- Likes / reactions on boards, pins, links or comments. Engagement counts can't be inflated through the API — they only change when real users interact through the app.
- View counts (
views_count,likes_count,comments_count) — read-only. - Follows, comments, votes, or notifications on other people's content.
- Other users' boards, pins, or profile data — even if public. Use the regular website to browse those.
- Admin / moderation actions (bans, reports, plan changes, audit logs).
If a request looks like an attempt to write to one of these, you'll get 403 or 404.
Pin variants, widgets & styles
pin_variant)"pin-01" | "pin-02" | "pin-03" | "pin-04" | "pin-05"
"pin-06" | "pin-07" | "pin-08" | "pin-09" | "pin-10"Default is "pin-01". Unknown values fall back to pin-01 when rendered.
style object — full schema{
paper: "plain" | "lined" | "grid" | "aged" | "kraft", // default "plain"
// "grid","aged","kraft" require Premium pin styles
tornEdges: { top: boolean, right: boolean, // default all false
bottom: boolean, left: boolean },
attachment: {
type: "pushpin" | "tape" | "clip" | "none", // default "pushpin"
// "tape" & "clip" require Premium pin styles
color: "#RRGGBB" | null // hex, optional
},
opacity: number, // 0.9 – 1.0, default 1
tint: "#RRGGBB" | null, // overlay tint, optional
widget: { // optional embedded widget (see below)
type: "image" | "text" | "youtube" | "spotify" | "audio"
| "link" | "quote" | "code" | "map" | "checklist" | "murder-mystery",
data: { /* widget-specific payload */ }
} | null,
width: number | null, // px, optional explicit size
height: number | null
}Plan-gated fields (premium paper textures, tape/clip attachments) are accepted in the request but will only render to viewers on a plan that allows them. The board owner's plan is what counts, not the API caller's.
style.widgetdata shape. Send only the fields listed for that type.// image — extra image inside the pin
{ type: "image", data: { url: string, alt?: string } }
// text — rich text block
{ type: "text", data: { content: string, align?: "left"|"center"|"right" } }
// youtube — embedded video
{ type: "youtube", data: { videoId: string, start?: number } }
// spotify — embedded track/playlist/album
{ type: "spotify", data: { url: string } }
// audio — direct audio file
{ type: "audio", data: { url: string, title?: string } }
// link — preview card
{ type: "link", data: { url: string, title?: string, description?: string, image?: string } }
// quote — pull quote
{ type: "quote", data: { text: string, author?: string } }
// code — code snippet
{ type: "code", data: { code: string, language?: string } }
// map — pinned location
{ type: "map", data: { lat: number, lng: number, label?: string, zoom?: number } }
// checklist — interactive checklist
{ type: "checklist", data: { items: { text: string, checked?: boolean }[] } }
// murder-mystery — Katil Kim? multiplayer lobby launcher
// maxPlayers: integer 4–12 (default 8), discussionSeconds: integer 30–300 (default 90)
{ type: "murder-mystery", data: { maxPlayers: 8, discussionSeconds: 90 } }
// Game sessions are created from the saved card. Do not put session tokens or roles in pin data.Some widget types (audio, code, map, checklist, embeds) are considered advanced widgets and require a plan with can_advanced_widgets. Unauthorized widgets are rejected with 400.
curl -X POST \
-H "Authorization: Bearer ptp_live_..." \
-H "Content-Type: application/json" \
-d '{
"board_id":"BOARD_UUID",
"name":"Demo video",
"pos_x":200, "pos_y":160, "rotation":-3,
"pin_variant":"pin-04",
"style": {
"paper":"lined",
"attachment":{"type":"tape","color":"#facc15"},
"tornEdges":{"top":false,"right":false,"bottom":true,"left":false},
"widget":{
"type":"youtube",
"data":{"videoId":"dQw4w9WgXcQ","start":0}
}
}
}' \
https://corkboard.social/api/public/v1/pinsSchemas
{
id: string // uuid
title: string
slug: string
description: string | null
visibility: "public" | "unlisted" | "private"
pins_count: number
links_count: number
views_count: number
created_at: string // ISO 8601
updated_at: string // ISO 8601
}{
id: string // uuid
board_id: string // uuid
slug: string
name: string
photo_url: string | null
short_desc: string | null
badges: string[]
pos_x: number
pos_y: number
rotation: number // degrees
pin_variant: string // "pin-01" … "pin-10"
style: PinStyle | null // see Styles & widgets section
likes_count: number
comments_count: number
created_at: string // ISO 8601
updated_at: string // ISO 8601
}{ "error": "Human-readable message" }Errors & limits
Errors come back as { error: string } with these status codes:
400— invalid input401— missing or invalid Bearer token403— token missing required scope404— resource not found, or you don't own it500— unexpected server error
A key only sees resources owned by the user who created it. Revoked keys stop working immediately.