Examples
These examples use two environment variables, the same ones the Homestead agent skill reads:
export HOMESTEAD_URL=http://localhost:8080export HOMESTEAD_TOKEN=hst_YOUR_TOKENThe token is an agent token with the Helper preset, which covers everything here. Create
one with homestead token create --name curl (see Connect an agent).
A member’s hsd_ device token works for the first four examples; lease, ingest and complete
accept agent tokens only. IDs below are
placeholders; use the ones your server returns.
Health
Section titled “Health”No authentication needed.
curl -sS "$HOMESTEAD_URL/api/v1/health"{"status":"ok","version":"dev"}Resolve pasted text
Section titled “Resolve pasted text”POST /resolve turns free text into candidates. It never creates anything. Needs
people:write. Set lookupProfiles to false to skip fetching public names and avatars.
curl -sS "$HOMESTEAD_URL/api/v1/resolve" \ -H "Authorization: Bearer $HOMESTEAD_TOKEN" \ -H "Content-Type: application/json" \ -d '{"input":"https://bsky.app/profile/alice.bsky.social\nhello world","lookupProfiles":false}'Reply (abridged):
{ "candidates": [ { "key": "c1", "input": "https://bsky.app/profile/alice.bsky.social", "platform": "bluesky", "handle": "alice.bsky.social", "profileUrl": "https://bsky.app/profile/alice.bsky.social", "confidence": "high", "existing": null, "suggestedPersonId": null, "warnings": [] } ], "unrecognized": ["hello world"]}A bare handle comes back with "confidence": "low" and alternatives. Pass hintPlatform to
choose for it. warnings holds i18n keys such as addWarningNeedsAgent.
Quick-add people
Section titled “Quick-add people”POST /quick-add creates people and accounts in one atomic step. It requires an
Idempotency-Key. Here, Alice is created with her Bluesky account, and the second entry attaches
an Instagram account to the person the first entry created (sameAsEntry: 0).
curl -sS "$HOMESTEAD_URL/api/v1/quick-add" \ -H "Authorization: Bearer $HOMESTEAD_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: add-alice-1" \ -d '{"entries":[ {"platform":"bluesky","handle":"alice.bsky.social", "person":{"new":{"displayName":"Alice","kind":"friend","closeness":2}}, "circleIds":["cir_01j9ebw22bbvzap1dk44j2y9eh"]}, {"platform":"instagram","handle":"alice.cooks", "person":{"sameAsEntry":0}} ]}'The reply has people (full person objects with their accounts), createdPeople,
createdAccounts and skipped. Sending the same request with the same key again returns the
same reply. The same key with a different body returns 409 idempotency_conflict.
Read the feed
Section titled “Read the feed”GET /feed needs feed:read. Leave out circleId for “All”.
curl -sS "$HOMESTEAD_URL/api/v1/feed?limit=20" -H "Authorization: Bearer $HOMESTEAD_TOKEN"The reply has entries, nextCursor, seed and generatedAt. Each entry has a type:
type | Holds |
|---|---|
moment | moment: one post or story |
hearth_pick | pick: a visit suggestion with person, account, reason and copyVariant |
caught_up | unseenRemaining: 0: everything unseen has been shown |
section | section: "earlier": seen moments from the last 3 days follow |
recent_visits | People recently visited with the companion (first page only) |
Pass nextCursor back as cursor for the next page until it’s null. Agents never mark
moments seen.
A fetch round
Section titled “A fetch round”This is what an agent does every few hours. It needs tasks:work and ingest:write.
-
Lease due tasks for the platforms your browser is logged in to. A lease lasts 10 minutes.
Terminal window curl -sS -X POST "$HOMESTEAD_URL/api/v1/agent/tasks/lease" \-H "Authorization: Bearer $HOMESTEAD_TOKEN" \-H "Content-Type: application/json" \-d '{"platforms":["instagram","douyin"],"limit":5}'Each task has an
id,kind(fetchorlookup), theaccount(withprofileUrl),since,knownItemIdsto skip,wantKinds,maxItemsandinstructionsnaming the playbook, such asinstagram.fetch.v1. Iftasksis empty, stop and come back afterretryAfterSeconds. -
Open the profile in the agent’s browser and follow the playbook. Collect up to
maxItemsmoments newer thansince, skippingknownItemIds. -
Upload media the browser already loaded. You get back a
mediaId. (Or pass the mediaurldirectly if its host is on the platform’s media host list.)Terminal window curl -sS "$HOMESTEAD_URL/api/v1/media/uploads" \-H "Authorization: Bearer $HOMESTEAD_TOKEN" \-F file=@/tmp/story-1.jpg -F kind=image -
Ingest the moments. Use
task id + ":" + page numberas the idempotency key.Terminal window curl -sS "$HOMESTEAD_URL/api/v1/ingest" \-H "Authorization: Bearer $HOMESTEAD_TOKEN" \-H "Content-Type: application/json" \-H "Idempotency-Key: tsk_01j9a1b2c3d4e5f6g7h8j9k02t:1" \-d '{"account":{"id":"acc_01j9te0skfktnnmw6zvew46azb"},"taskId":"tsk_01j9a1b2c3d4e5f6g7h8j9k02t","items":[{"platformItemId":"3456789012345678901","kind":"story","url":"https://www.instagram.com/stories/lin.cooks/3456789012345678901/","publishedAt":"2026-10-17T07:12:00Z","media":[{"mediaId":"med_01j9a1b2c3d4e5f6g7h8j9k01w"}]}]}'The reply has a
batchId, theacceptedandduplicatescounts,momentIds, andrejecteditems with anindexand acode. Valid items are stored even if some are rejected. -
Complete the task with the batch IDs. Other statuses are
login_required,blocked,not_found,rate_limited(withretryAfterSeconds) anderror.Terminal window curl -sS -X POST "$HOMESTEAD_URL/api/v1/agent/tasks/tsk_01j9a1b2c3d4e5f6g7h8j9k02t/complete" \-H "Authorization: Bearer $HOMESTEAD_TOKEN" \-H "Content-Type: application/json" \-d '{"status":"ok","ingestBatchIds":["ing_01j9a1b2c3d4e5f6g7h8j9k02g"]}'A successful completion returns
204.
Wait at least 20 seconds before the next profile on the same platform, and stop after 15 minutes or when no tasks are left.
What ingest checks
Section titled “What ingest checks”kindmust be one the platform produces, andurlmust be on the platform’s own hosts.publishedAtcan’t be more than 10 minutes in the future. Stories older than 48 hours are rejected astoo_old.textis stripped of HTML and control characters, up to 5000 characters.- Media URLs must match the platform’s media hosts, or the item is rejected as
media_host_not_allowed. The server downloads them itself, up to 50 MB in 30 seconds. - A repeated
platformItemIdfor the same account counts as a duplicate, and its text and media are updated if they changed.