You publish a pin, notice a typo in the title ten minutes later, and send a PATCH. PinBridge answers 409 pin_already_published.
Once a pin is live on Pinterest, PinBridge can’t change it. Before that point you can change almost anything, including the time, the board, the link and even the image. The trick is to catch the mistake while the pin is still in one of those windows, and to know what to do when it isn’t.
Why a live pin is read-only
Pinterest’s v5 API has an update pin endpoint, but Pinterest treats it as a restricted feature and enables it per app. An app without access gets 401 with code 3: “Your application does not have access to this restricted feature.” PinBridge’s Pinterest app doesn’t have that access.
If you call Pinterest directly and hit that error, the user’s token is fine. Reconnecting the account won’t help, because the restriction sits on the app, not on the user.
PinBridge doesn’t send the request to Pinterest just to see it fail. A PATCH on a published pin is refused before anything leaves PinBridge, and the refusal is written to the activity log as pin.update_rejected. This is the real response from our sandbox, trimmed to the error object:
{
"error": {
"code": "pin_already_published",
"message": "This pin is already published on Pinterest and cannot be edited: Pinterest's API does not allow PinBridge to edit published pins.",
"remediation": "Delete the pin and publish a corrected one, or edit it on Pinterest directly.",
"pin_id": "86133e40-bda3-4202-86a6-aa6766f4a9eb",
"pinterest_pin_id": "536368337961064032",
"request_id": "afb870cd-7847-4b4b-a145-04d5813009b2"
}
}
The remediation field matters most when the caller is a script or an AI agent. It says what to do next, so the caller doesn’t retry the same edit forever.
Where you can still edit
Each state on the way to Pinterest has its own rules:
| Where the pin is | What you can change | Call |
|---|---|---|
| Not sent yet | Everything | POST /v1/pins/validate (dry run) |
| Scheduled, not yet run | Time, board, title, description, link, image or video, video cover | PATCH /v1/schedules/{id} |
| Queued or deferred | Title, description, link, alt text, board | PATCH /v1/pins/{id} |
| Publishing | Nothing. Wait a few seconds | 409 pin_publishing |
| Failed | Title, description, link, alt text, board, then retry | PATCH /v1/pins/{id}, then POST /v1/pins/{id}/retry |
| Published | Nothing through the API | 409 pin_already_published |
The cheapest fix happens before anything is sent. A dry run runs the same account, board, media and quota checks as a real publish and writes nothing.
A pin you publish right away sits in the queue for seconds, so in practice you won’t catch it there. The exception is a deferred pin, parked after repeated retryable errors and waiting about an hour for its next attempt. Most real edits happen on schedules.
Move a scheduled pin instead of cancelling it
The usual habit is to cancel the schedule and create a new one. You get a new id and lose the history, and your own database still points at the cancelled schedule. PATCH /v1/schedules/{id} edits the pending schedule in place.
Here a schedule set for 8 October at 09:00 Paris time moves to the 9th at 18:30, with a new title and a tagged link:
curl -X PATCH "https://api.pinbridge.io/v1/schedules/355dfc95-4cfa-40eb-93c3-f34c7fb65449" \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"run_at": "2026-10-09T18:30:00+02:00",
"title": "Autumn wool collection, back in stock",
"link_url": "https://example.com/wool?utm_source=pinterest"
}'
{
"id": "355dfc95-4cfa-40eb-93c3-f34c7fb65449",
"status": "scheduled",
"run_at": "2026-10-09T16:30:00Z",
"pin_id": null
}
The schedule keeps its id and stays scheduled, with the new time returned in UTC. The edit goes through the same checks as creating a schedule: run_at must be in the future and carry a timezone, a new board is checked for publish access, and new media is resolved before the call returns. PinBridge locks the schedule while it applies the change, so the scheduler can’t pick it up halfway through.
Once the schedule has run, the call returns 409 schedule_not_editable with a remediation that points to the next step. If the pin it created hasn’t published yet, edit that pin (its pin_id). If it failed, retry it. If it’s live, replace it.
Fix a failed pin, then retry it
A failed pin keeps its payload. Fix the field that caused the failure and send it again:
curl -X PATCH "https://api.pinbridge.io/v1/pins/$PIN_ID" \
-H "X-API-Key: $PINBRIDGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"link_url": "https://example.com/linen"}'
curl -X POST "https://api.pinbridge.io/v1/pins/$PIN_ID/retry" \
-H "X-API-Key: $PINBRIDGE_API_KEY"
If the board was the problem, retry also accepts a board_id (or a different account_id), and PinBridge checks the new target before it requeues the pin. The retries and failures guide explains which errors PinBridge retries on its own and which ones stop at failed. If you already receive webhooks, pin.failed is a good trigger for this fix.
When the pin is already live
You have two options, and the right one depends on what’s wrong.
Edit it by hand on Pinterest. The account owner can change a pin’s title, description, link and board in the Pinterest app. The pin keeps its saves and its stats. PinBridge’s record keeps the values it published, so your data and Pinterest will disagree from then on. That’s fine for a typo on a pin that’s already doing well. It doesn’t scale, and nobody can change a pin’s image after it’s published.
Replace it. Publish a corrected pin, then delete the old one. Three details matter:
- Pass
delete_from_pinterest=true. Through REST and the Python SDK, a plain delete removes only the PinBridge record and leaves the pin live on Pinterest. The MCPdelete_pintool works the other way round: it removes the pin from Pinterest unless you tell it not to. - Use a new idempotency key. PinBridge keeps a record of deleted pins, and a key stays tied to its pin for good. Send the old key again and you get the old pin back instead of a new one. Derive the new key from the corrected content, so retrying the fix can’t post twice.
- Publish first and delete second. If something breaks between the two steps, you end up with one pin too many, which is easy to clean up, rather than none at all.
The replacement starts from zero. Saves, clicks and impressions belong to the old Pinterest pin and don’t move over. For a wrong link or a wrong image, that’s usually a price worth paying.
Here’s the whole flow as one function with the Python SDK. It edits the pin in place when it can and replaces it only when it has to:
import hashlib
import json
import os
from pinbridge_sdk import APIError, PinbridgeClient
client = PinbridgeClient(api_key=os.environ["PINBRIDGE_API_KEY"])
FIELDS = ("title", "description", "link_url", "alt_text", "board_id")
def correct_pin(pin_id: str, **changes) -> str:
"""Apply changes to a pin. If it is already live, publish a corrected copy
and remove the old one. Returns the id of the pin to keep."""
try:
return str(client.pins.update(pin_id, changes).id)
except APIError as exc:
if exc.code != "pin_already_published":
raise # pin_publishing: wait a few seconds and call again
old = client.pins.get(pin_id)
fixed = {f: changes.get(f, getattr(old, f)) for f in FIELDS}
digest = hashlib.sha256(json.dumps(fixed, sort_keys=True).encode()).hexdigest()[:12]
new = client.pins.create({
**fixed,
"account_id": str(old.pinterest_account_id),
"image_url": old.image_url,
"idempotency_key": f"{old.idempotency_key}:fix-{digest}",
})
client.pins.delete(pin_id, delete_from_pinterest=True)
return str(new.id)
correct_pin("c88beb71-4bad-4666-a9ba-af660bce79a7", title="Spring linen collection")
We ran this against a sandbox pin that had already published. It returned a new pin with the corrected title, the old pin answered 404, and repeating the create step with the same content returned the same new pin rather than a second one. Store the returned id wherever you kept the old one. The function covers image pins published from a public URL; for an uploaded file, pass asset_id instead of image_url.
The same rules for AI agents
Agents connected through the PinBridge MCP server get the same contract. update_pin is described to the model as an edit for pins that haven’t published yet, update_schedule moves a pending schedule in place, and the delete_pin description tells the agent to confirm with you before it deletes anything. An agent that tries to edit a live pin gets pin_already_published with the remediation attached, so it can lay out the choice for you instead of repeating the call.
Short version
- Catch mistakes before anything is sent, with a dry run.
- Edit schedules in place with
PATCH /v1/schedules/{id}instead of cancelling and recreating them. - Queued, deferred and failed pins accept
PATCH /v1/pins/{id}. - A published pin is read-only through the API. Fix it by hand on Pinterest, or publish a correction under a new idempotency key and then delete the old pin with
delete_from_pinterest=true.
You can try every one of these calls in the sandbox without touching a real Pinterest account. Start free and make your next fix while the pin is still a schedule.
