POST /tasks/pull

Pull open edits, oldest first. Pulling a task's batch locks it to this agent until it is acked or released.

/docs/api/pull.txt

POST https://api.pixydesignapp.com/tasks/pull

Pull open records, oldest first. This is the call an agent lives on, the loop around it is the agent workflow.

A bare pull carries everything that is work: the user's tasks, when they packaged any, and the loose records they never packaged, of the site and its rules. Loose records get filed into a task by you, at ack, under a name.

What stands on the canvas, beside the pages, is not the site: a component the user builds there before it has a home. A bare pull leaves it out, with the rules made for it. It comes when the user packages it into a task, a task ships whatever it holds, or when you ask for it with include.

Pulling a task's batch locks it: the task turns working, pinned to this agent, and other agents cannot pull it until it is acked or released. A bare pull walks past tasks another agent is already working on.

Reading is not resolving. A record stays new until it is acked, so an unacked batch comes back on the next call instead of vanishing. A pull that delivers records also tells every open editor an agent is building them.

Header: X-Pixy-Key carrying the agent key (px_sk_…).

Body (every field optional, an empty body pulls everything open):

field type description
task string One task's id. Asking for a task another agent holds is refused outright, rather than answered with an empty batch that would read as nothing to do.
url string One page's records, plus the site's rules, because a rule applies on that page too.
selector string One element, by its xpath or its id, or one rule, by its selector.
ids array Explicit record ids.
include array Which kinds of loose records: site, elements standing on a page and the removal of one kept on the canvas as a draft, rules, the site's rules, canvas, what stands beside the pages and the rules made for it. Unsaid, ["site", "rules"]. ["canvas"] is the canvas alone, ["site", "rules", "canvas"] everything. A task's records ship whatever the list says.
take boolean Stamp the batch as pulled. An audit marker, not a status: it does not resolve anything.

Response:

field type description
count number How many records came back.
edits array The records themselves, oldest first. Each carries the id that /tasks/ack takes back.
tasks array Every task the batch belongs to, each with its id, title, status and messages, the thread. Read the conversation before building. A record's task_id says which of them it is in, none for a loose one.
media object Present when the records reference files: how many, and the media.zip call that fetches them.
excluded object Present when include left loose records out: how many, by kind, {"canvas": 30}. Tell the user, so an empty pull never reads as nothing to do while a component waits on the canvas.

The record

A record is what the edits made of one target: what each changed key is now, what the site gave it, and nothing of how it got there. Every field is the editor's own.

field what it says
id The record's id, what ack takes back.
type element, one element, or rule, one CSS rule, which reaches every element it matches. A rule the site wrote is the site's. A rule made in Pixy, its snapshot.css empty, is the look of a class the user gave: it is the site's once an element it was made for stands on a page, and the canvas's while they all stand on the canvas.
target The address. An element's xpath in the page as the site has it, from the nearest ancestor with an id of the site's. A born element's own id, since no source exists for it yet. A rule's selector.
url The page the record was made on. null on a rule, which reaches every page, and on an element made on the canvas. Where an element stands now is its move, and that decides its kind: one made on the canvas and moved into a page is site.
xpath, selector An element's address the two ways DevTools copy it, both from the nearest ancestor with a site id. null on a born element.
home Where the site stands the element: the page, the container as a placement names one (below), and its index among the container's children. A born element has none.
picked A rule's: the element it was picked on, its url, xpath, selector and html, a copy to find the rule by.
snapshot The target as the site had it. An element's html, its markup, and css, every declaration that styled it as the site's author wrote it. A born element's html is the whole element to make. A rule's css is the rule's own declarations.
preview The element's text, or its kind, to name it in a list.
changes What the edits made of the target, below.
status new. What an ack turns into resolved or dismissed is not pulled again.
task_id The task the record is filed in, when it is.
site The host the record was last changed on.

changes is a list with one entry, what the edits made of the target for the default conditions:

key what it says
set Every style property the edits gave the target a value for, by its name, every attribute, html when what is written inside the element changed, and tag when the element was renamed. A key set back to what the site gave it is not here.
unset Every style the site declares and every attribute it wrote that the edits took off.
original What the site gave each key in set and unset before the first edit touched it, "" for none. What to look for in the source.
move Where the element stands now, when that is not its home: url, the container, and index among its children. A born element's move is from nowhere to where it was put down. "removed": the element was removed from the page, remove it from the source. Whatever else its set and unset say no longer matters.
context screen_size, the width and height the change was designed at, and theme, light or dark. Map the width to the project's breakpoints: a base style or a media query.
timestamp When the entry last changed.

class in set is the whole attribute, every class the element wears now. Build only the difference from original.class: add the classes that are new, take off the ones that went, and leave the rest as the source has them. A class made in Pixy with no rule of its own is a new rule to write.

tag in set is the element's tag now, original.tag the one the site has, and the one its address still names. It is the same element renamed: change only the name, keep its attributes and contents.

What stands in a removed element is not pulled, and neither is a born element that was removed: the site never had it.

One of the site's elements dragged onto the canvas is a draft: the user keeps it there and wants it off the page. It reaches you as "removed", like any removal. Once you ack it resolved, the draft stays on the canvas as an element of its own, and putting it back on a page later is a new element to build. Put back before you built, it is the site's own element again and no removal arrives.

A placement names a container one of three ways: parent is a record's id, an element with a record of its own, a born one or a group; parent: null is the canvas itself; otherwise xpath and selector name one of the site's elements. Move the markup and let the layout do the rest. On the canvas a placement also carries the box the workspace drew, left, top, width, height: where the user keeps it while building, not where it goes on the site.

An intent is an attribute in set starting with data-pixy-: a choice made in Pixy for you to build the site's own way, never markup to ship. The workflow lists them.

A site element restyled, one property on one element:

{
  "id": "k7f2…", "type": "element", "url": "/pricing",
  "target": "//*[@id=\"plans\"]/div[2]/p[1]",
  "xpath": "//*[@id=\"plans\"]/div[2]/p[1]", "selector": "#plans > div:nth-of-type(2) > p:nth-of-type(1)",
  "snapshot": {"html": "<p class=\"price\">$29</p>", "css": ".plan > .price { font-size: 24px; font-weight: 600 }"},
  "preview": "$29",
  "changes": [{"timestamp": "2026-09-28T09:12:03.412Z",
    "context": {"screen_size": [1440, 900], "theme": "light"}, "conditions": {"state": "default"},
    "set": {"font-size": "32px"}, "unset": [], "original": {"font-size": "24px"}}],
  "status": "new", "site": "acme.com"
}

A rule, the same change for everything the selector reaches, implemented once at the component:

{
  "id": "b2f8…", "type": "rule", "url": null, "target": ".btn",
  "picked": {"url": "/", "xpath": "//*[@id=\"hero\"]/a", "selector": "#hero > a", "html": "<a class=\"btn\">Start</a>"},
  "snapshot": {"css": ".btn { padding: 8px 16px; border-radius: 4px }"},
  "changes": [{"set": {"padding": "12px 20px", "border-radius": "6px"}, "unset": []}],
  "status": "new"
}

A born element, drawn or put down in Pixy, its markup in the snapshot and its slot in move:

{
  "id": "m2a9…", "type": "element", "url": "/", "target": "m2a9…", "xpath": null, "selector": null,
  "snapshot": {"html": "<div data-pixy-layout=\"auto-grid\"><h3>Plans</h3><p>Pick one.</p></div>"},
  "changes": [{"set": {"padding": "24px", "data-pixy-layout": "auto-grid"}, "unset": [],
    "move": {"url": "/", "xpath": "//*[@id=\"main\"]/section[2]", "selector": "#main > section:nth-of-type(2)", "index": 1}}],
  "status": "new"
}

A site image changed for a file: src set to the stored file, the responsive attributes unset, the bytes in media.zip:

{
  "id": "e41c…", "type": "element", "url": "/", "target": "//*[@id=\"hero\"]/img",
  "snapshot": {"html": "<img src=\"/img/hero.jpg\" srcset=\"/img/hero@2x.jpg 2x\" alt=\"Hero\">"},
  "changes": [{"set": {"src": "media/hero.webp", "data-pixy-crop": "120 40 800 600"}, "unset": ["srcset", "sizes"]}],
  "status": "new"
}

A background from a file: the style names the stored file, the bytes in media.zip as for any image:

{"changes": [{"set": {"background-image": "url(\"media/texture.webp\")", "background-size": "cover"}, "unset": []}]}

A move, and a removal:

{"changes": [{"set": {}, "unset": [], "original": {}, "move": {"url": "/", "parent": "m2a9…", "index": 0}}]}
{"changes": [{"set": {}, "unset": [], "original": {}, "move": "removed"}]}

Errors:

error when
invalid_key The key is not one of the project's, or was retired.
locked The named task is held by another agent. The answer carries agent_name, who holds it.
invalid_include A name in include is not site, rules or canvas.

Examples:

Everything open, nothing locked by other agents:

curl -X POST https://api.pixydesignapp.com/tasks/pull \
  -H 'X-Pixy-Key: px_sk_yourkey' \
  -H 'Content-Type: application/json' \
  -d '{}'

One task's batch, stamped as pulled:

curl -X POST https://api.pixydesignapp.com/tasks/pull \
  -H 'X-Pixy-Key: px_sk_yourkey' \
  -H 'Content-Type: application/json' \
  -d '{"task": "9f1c2e40-7b3a-4d51-9a2e-2c8f0b6d1a77", "take": true}'

One page, one rule:

curl -X POST https://api.pixydesignapp.com/tasks/pull \
  -H 'X-Pixy-Key: px_sk_yourkey' \
  -H 'Content-Type: application/json' \
  -d '{"url": "/pricing", "selector": ".plans > .plan > .price"}'

What was built on the canvas, a component with no home yet:

curl -X POST https://api.pixydesignapp.com/tasks/pull \
  -H 'X-Pixy-Key: px_sk_yourkey' \
  -H 'Content-Type: application/json' \
  -d '{"include": ["canvas"]}'