For agents

Knickknacks is a few small tools your agent can use to work with people. There's no built-in assistant: your agent brings the thinking. The first tool is forms.

Get a key

Two ways. A person can sign in and make a key on the Agents page, which gives them a setup prompt to paste. Or the agent signs itself up:

POST https://knickknack.omarsjunk.com/v1/agent_signup
{ "email": "person@example.com", "agent": "Claude" }

202 { "status": "pending_approval", "poll_secret": "...", ... }

# The person gets an email and taps Approve. Meanwhile, every few seconds:
POST https://knickknack.omarsjunk.com/v1/agent_signup/claim
{ "poll_secret": "..." }

200 { "status": "approved", "api_key": "kk_live_...", ... }   # shown once

Ask the person for their email first, and tell them to look for a message from Knickknacks (check spam). The key can't be claimed until they approve, and it is shown once. Send it as Authorization: Bearer <key>.

Forms

EndpointScopeUsed for
GET /meanyWho the key acts as.
POST /formsforms:writeMake a form. Returns the public url to give the person.
GET /formsforms:readYour forms, with answer counts.
GET /forms/{id}forms:readOne form, its fields and status.
PATCH /forms/{id}forms:writeEdit, or close and reopen with closed: true/false.
DELETE /forms/{id}forms:writeArchive. The link stops working; answers are kept.
GET /forms/{id}/responsesforms:readThe answers, oldest first.

Making a form

POST https://knickknack.omarsjunk.com/v1/forms
Authorization: Bearer kk_live_...
Idempotency-Key: optional, unique to this write

{
  "title": "Saturday dinner",
  "description": "Who's in?",
  "fields": [
    { "label": "Your name", "type": "short_text", "required": true },
    { "label": "Can you come?", "type": "yes_no", "required": true },
    { "label": "Anything you can't eat?", "type": "long_text" },
    { "label": "Which night", "type": "single_choice", "options": ["Friday", "Saturday"] }
  ],
  "closes_at": "2026-12-01",
  "max_responses": 20,
  "confirmation_message": "Thanks! See you there."
}

201 { "id": "...", "url": "https://knickknack.omarsjunk.com/f/...", "fields": [ { "key": "your_name", ... } ], ... }

Field types: short_text, long_text, number, email, date, yes_no, rating, single_choice, multi_choice. Each field takes label (required), type (required), required, help, and key (made from the label if you leave it out). Choice fields need options (2 to 30). Number fields can take min and max. Ratings are 1 to 5. A form can have up to 50 fields.

Once people have answered, you can still retitle, relabel, add fields and close the form, but you can't remove a field or change its key or type (you get form_has_responses).

Reading the answers

GET https://knickknack.omarsjunk.com/v1/forms/{id}/responses?since=0

200 {
  "fields": [ { "key": "your_name", "label": "Your name", "type": "short_text" }, ... ],
  "responses": [
    { "number": 1, "submitted_at": "...", "answers": { "your_name": "Maya", "can_you_come": true } }
  ],
  "next_since": 1,
  "has_more": false
}

Keep next_since and pass it as since next time: each answer comes back exactly once. If has_more is true, call again straight away. Answers are anonymous unless the form asks for a name or email, so add a field when the person needs to know who replied. Yes/no answers are true/false, multiple choice is a list, numbers are numbers.

Good manners

Draft the questions with the person before you create a form. Show them the link. Only report answers people actually submitted, and never fill a form in yourself. Errors come back as { "message", "code" }; the message is written to be passed on as it is.

Machine-readable

/openapi.json · /llms.txt