one hundred ways to put one line about a song to work. six are files you can run tonight; ninety-four are instructions with the exact call under each, and the last four, the full house, walk most of the doors in one flow. everything here is plain http against the doors below — no library, no account beyond a key where something is filed.
house rules, in three lines: the wall is public, so every line filed is readable by anyone. quote a line exactly and name its writer. never file words a person did not write.
the resources · the doors
every door the recipes go through. click one to see only the recipes that use it; the call is the exact shape.
GET /api/hermit/read?song=<id> · ?url=<spotify link>needs nothing
one line about a song: a human take first (credited), the house voice only when nobody has filed one. &format=text for the bare line · &community=1 filed lines only, free · &fresh=1 skips the cache, metered — past the daily allowance, one credit per fresh read for members and owned keys. more →
GET /api/hermit/card?song=<id> · ?take=<id> · &format=card|story|square|discordneeds nothing
the read typeset on the wall’s index card, as a png. 1200×630 · 1080×1920 · 1080×1080 · 1040×440. &compact=1 stays under bluesky’s cap. more →
GET /api/hermit/playlist?ids=<id,id,…> · &format=cardneeds the site, a sign-in or a key (the card: nothing)
one line per track for up to 30 tracks, human lines first, the house voice on misses (at most 6 fresh per call). &format=card is a 1080×1350 grid. off the site the records come as spotify links, without names; ten calls per network per ten minutes. more →
GET /api/v1/takes?song=<id or link>needs nothing
every human line filed on a record, newest first, up to 50, each with its writer, votes, element and permanent /t/ address. free, always. more →
GET /api/record/<spotify id>needs nothing
what the song page itself loads: the song, its lines with votes and elements, the house’s line, and sibling records. free. more →
POST /api/v1/takes { spotifyId|url, text, element?, author? } · x-api-keyneeds a key
file one line, 1–280 characters, no links, about one part of the song (sound · lyrics · title · canvas · art). the same gate the site uses: 60 a day per key, 10 a day per writer. more →
POST /api/explore { query }needs nothing
a feeling in words in; the moods it maps to and the human lines that match out, each with its song, artist and spotify link. more →
https://open.spotify.com/embed/track/<spotify id> (the read’s doors.embed)needs a browser
spotify’s own player, for a record’s thirty seconds. the sound plays there and only there: never another service’s clip beside spotify’s cover or song details, which spotify’s rules forbid. more →
GET /api/daily-songneeds nothing
today’s song, its top line, how many have been filed, and when it rolls over (midnight utc). more →
GET /api/battles/todayneeds nothing (voting is on the site)
two lines on one record, today. read it anywhere; the votes are cast on the wall, signed in. more →
GET /api/qr?text=<a photoprompting.org address>needs nothing
an svg qr code for any on-site address — a song page, a take, a diary. it refuses anything off-site. more →
GET /api/oembed?url=<take url>&format=jsonneeds nothing
oembed 1.0: paste a /t/ link into anything that speaks it (wordpress, ghost, notion) and the card renders. the framable card is /embed/take/<id>. more →
the take on a track inside an iframe on any page. a track id, a spotify link, or a song uuid. more →
POST /api/hermit/prose { form, topic, length? } · x-user-token or x-api-keyneeds sign-in or a key
longer writing in the voice, one draft. forms: about · vignette · review · riff · launch-post · micro-essay · story. answers as { prose }. 8 per 10 minutes, and a day’s ceiling for the whole house. since 21 october a signed-in member or a key: an open door here was a free model for anyone. more →
POST /api/hermit/audition { text, songTitle?, songArtist? }needs nothing
one line written as the voice; the distilled profile answers { score 0–100, verdict, tell }. 6 per 10 minutes. more →
GET /api/hermit/voice-profileneeds nothing
the distilled personality — summary, moves, vocabulary, avoids — with the corpus size and the week it was distilled. it changes on mondays. more →
POST /api/hermit/interpret { spotifyId }needs nothing
the read with its paperwork: source (community or model), provenance (the take and writer, or the model and exemplar count), quality, cached or not. more →
POST /api/v1/voice { prompt, form?, length?, best?, voice_version? } · x-api-keyneeds a key
the commercial door. metered by the key’s daily quota, then by credits; every answer stamped with voice.version and corpus_takes; best:true is three drafts and a judge for 3 units. more →
https://photoprompting.org/api/mcp (streamable http, one sign-in screen)needs an assistant that speaks mcp
the hermit as a tool in claude, chatgpt, cursor or claude code: find_song · read_song · speak_take · song_card · read_playlist · takes_on_song · file_take. a key works as a bearer header too. more →
/botneeds a discord server
drop a spotify link in a channel, get the line and the 30 seconds. /take files one; /desk is for artists. more →
/s/<spotify id> · /t/<take id>needs nothing
one page per record and one per line — the art, the 30 seconds, every take, the door to write yours. link them, print them, put them in a qr. more →
GET /api/till · /api/till/go?sku=<id>needs a card
keys, credits, desk reads and tips, by card. the catalog is live at /api/till; a plain link starts a purchase from anywhere. more →
GET /api/credits/balanceneeds sign-in
your credits, as a number, plus how many keys are tied to your account. one credit covers one fresh read past the daily allowance on any metered door. the same figure sits on /credits and your profile row. more →
the resources · the reference
where the doors are documented, tried, and paid for.
the docsthe reference: every endpoint, every parameter, error shapes./docs
the doorsevery door tried live on one page — paste a link and watch them all answer./doors
openapi.jsonthe machine-readable contract; import it into anything that speaks openapi./openapi.json
llms.txtthe guide written for agents — when to reach for the wall and when not to./llms.txt
llms-full.txtthe long form of the same guide./llms-full.txt
the voice licensewhat outputs you own and what the corpus and voice are not./voice-license
the resources · the walls
what each door allows before it asks you to slow down. cached and filed reads are free everywhere.
the read
20 fresh reads a day per reader, from a public allowance of 150; members 12; keys their tier; then 1 credit per fresh read for members and owned keys. cached and filed reads are free and unlimited
the playlist
up to 30 tracks a call; at most 6 fresh reads per call
the filing
60 a day per key · 10 a day per writer · 30 a day per address · 300 a day overall
the prose
8 per address per 10 minutes
the audition
6 per address per 10 minutes
the licensed voice
a key’s daily quota (25 · 100 · 350 · 1200 by tier), then 1 credit per read; best-of-three is 3
the mcp server
anonymous passes share a small daily allowance of fresh reads; file_take is ten a day per handle
six you can run · 01–06
each a single file in /examples, retried on hiccups, nothing but fetch. the same six sit on the doors.
photography: a folder of pictures becomes print-ready wall labels — each photo paired with the record people wrote about that mood, one line, and a qr that plays the 30 seconds at the frame. the photographs never leave your machine.
you say one sentence about the song while the water runs; your assistant files it under your handle once you are dry. your words, not its.
how
by voice, to an assistant connected at https://photoprompting.org/api/mcp:
"file this on show me how: the bass line walks home the long way — about the sound"
→ file_take(song, line, part: "sound") → it reads the line back, you say yes, it answers with the /t/ address
doorsneeds · an assistant with the mcp server
11
the lunch audition
mornings & rituals
one line a day, judged. keep the scores in a spreadsheet and watch the tell change over a month.
how
curl -s -X POST https://photoprompting.org/api/hermit/audition -H 'content-type: application/json' \
-d '{"text":"this chorus files its taxes early","songTitle":"Show Me How","songArtist":"Men I Trust"}'
# → { "score": 0-100, "verdict": "…", "tell": "…" }
doorsneeds · nothing
12
the last song of the day
mornings & rituals
the final record you played, one line about it, appended to tonight’s note. a month of nights reads like a calendar.
how
ios shortcuts · "when sleep focus turns on":
Get Current Song → Get Contents of URL → https://photoprompting.org/api/hermit/read?url=<the share link>&format=text
Append to Note "nights"
doorsneeds · a phone
13
the saturday battle
mornings & rituals
today’s battle over breakfast. the household reads both lines and votes by show of hands; whoever picked the loser does the dishes.
write a line, audition it, rewrite until the tell stops naming the same flaw. three tries, then file the survivor.
how
audition → read the "tell" → rewrite → audition again (6 per 10 minutes)
then: POST https://photoprompting.org/api/v1/takes with the line that stopped getting the same note
doorsneeds · a key
19
the two-hand notebook
writing & journals
two people, one record, both file. the list shows who wrote what, and when, without either of you saying so.
the week’s record as the topic; the voice writes two hundred words; you write the rest of the essay against it.
how
curl -s -X POST https://photoprompting.org/api/hermit/prose -H 'content-type: application/json' \
-d '{"form":"micro-essay","topic":"Show Me How by Men I Trust, on a tuesday"}' | jq -r .prose
doorsneeds · sign-in or a key
21
the warm-up
writing & journals
before you read anyone else, write your own line on today’s song. then look at what the wall said, and notice the difference.
a chapter has a mood. ask for it in words; take the most-voted line as the epigraph, with the writer’s name under it.
how
curl -s -X POST https://photoprompting.org/api/explore -H 'content-type: application/json' -d '{"query":"leaving a party early"}' \
| jq -r '.takes | sort_by(-.votes)[0] | "\(.text)\n — \(.display_name), on \(.songs.title)"'
doorsneeds · nothing
23
the sentence test
writing & journals
audition your best sentence from any draft. it scores how much the sentence reads like a thing a person would say once. low scores usually mean you were explaining.
how
curl -s -X POST https://photoprompting.org/api/hermit/audition -H 'content-type: application/json' \
-d '{"text":"<one sentence from your draft>"}' | jq '{score, tell}'
doorsneeds · nothing
home, hardware & displays
frames, printers, speakers, a pi in the kitchen.6
24
the frame that matches the room
home, hardware & displays
each room has a word — kitchen: sunday, office: focus, hall: leaving. the frame asks for the word, takes the first record that matches, and shows its card.
when the kitchen speaker changes track, the little display shows the line in text form. nobody asked; it is just there.
how
configuration.yaml:
rest_command:
hermit_line:
url: "https://photoprompting.org/api/hermit/read?url={{ track_url }}&format=text"
automation: on media_player track change → hermit_line → show on the display
doorsneeds · home assistant
26
the doorbell
home, hardware & displays
the doorbell rings and the hallway speaker says one line about the house’s record. guests stop asking what it means by the third visit.
how
automation: doorbell pressed →
rest: https://photoprompting.org/api/hermit/read?song=0bu0to8kgrXDzWklgZ3BuQ&format=text
tts.speak: the response body
doorsneeds · home assistant
27
the record shelf
home, hardware & displays
a label per vinyl: the line, the credit, and a qr to the song’s page, where the 30 seconds play. the shelf becomes its own liner notes.
the pager fires and the incident channel gets the team’s six comfort records, one line each. the incident is still the incident; the channel is calmer.
the record beside the picture; the line on paper.8
38
the zine
photography, art & print
a photograph on the left page, the card of the record that matches its mood on the right. sixteen spreads, stapled.
how
node photos.mjs ./shots # the built recipe makes shots/wall.html
# or per spread:
curl -s "https://photoprompting.org/api/hermit/card?song=<id>&format=square" -o spread-01-right.png
doorsneeds · a printer
39
the album insert
photography, art & print
a musician prints the lines listeners filed on the release as the insert of the physical edition. credited, dated, in the order they arrived.
pick a mood; spotify’s player plays the record for about thirty seconds; that is your test-strip timer, with a line to read while the paper sits in the tray.
how
open "https://open.spotify.com/embed/track/75IQVo8hqI1iwVZyvkN2VT" # spotify’s player: when the thirty seconds end → pull the strip
doorsneeds · a darkroom
42
the caption
photography, art & print
the shoot had a mood. say it in words; the most-voted line on a matching record is the caption, with the writer’s name. nobody has to think of something to say under a photograph again.
your own lines, typeset as a book. the press at /print sets a manuscript from the wall; a year of lines is a volume.
how
https://photoprompting.org/print → manuscript
# or gather them yourself, one record at a time:
curl -s "https://photoprompting.org/api/v1/takes?song=<id>" | jq -r '.takes[] | select(.author=="you") | .text'
doorsneeds · a year of lines
44
the light and the grid
photography, art & print
eight long exposures from one night, each paired with the grid card of the eight records that played while the shutter was open.
gallery wall text in the voice: the form is "about", the topic is the show. your name under it; the voice’s version stamped in the corner if you like footnotes.
how
curl -s -X POST https://photoprompting.org/api/hermit/prose -H 'content-type: application/json' \
-d '{"form":"about","topic":"twelve photographs of the same bus stop","length":"short"}' | jq -r .prose
doorsneeds · sign-in or a key
games & parties
the wall as a deck of cards.8
46
the quiz round
games & parties
ten records in, ten lines out, titles stripped. teams name the record from the line alone. the house voice lines are the hard ones.
guests file one line each on the birthday person’s most-played record, under their own names. ten a day per writer, so nobody can dominate.
how
POST https://photoprompting.org/api/v1/takes {"spotifyId":"<their record>","text":"<the roast, one line>","author":"<guest>"}
# the wall for the toast: https://photoprompting.org/s/<their record>
doorsneeds · a key
48
the table battle
games & parties
today’s battle on the television; the table argues; someone signs in and casts the table’s vote.
one song, everyone writes a line, the audition scores them, the tells are read aloud in ascending order. the winner is not who you expect.
how
for line in "$@"; do
curl -s -X POST https://photoprompting.org/api/hermit/audition -H 'content-type: application/json' \
-d "$(jq -cn --arg t "$line" '{text:$t, songTitle:"Show Me How", songArtist:"Men I Trust"}')" | jq -c '{score, tell}'
done
doorsneeds · nothing
50
the invitation
games & parties
the night’s set as a grid card. that image is the invitation; the tracks are the promise.
every new state line, the driver names a song and each passenger files one line about it by voice. the trip is on the wall by the time you are home.
how
by voice: "file this on <song>: <one line> — about the sound"
→ file_take under each passenger’s handle, ten a day each
doorsneeds · an assistant with the mcp server
53
the blind date with a record
games & parties
ask for a mood, hide the titles, play each record in spotify’s player. guess the artist from thirty seconds and one line.
how
curl -s -X POST https://photoprompting.org/api/explore -H 'content-type: application/json' -d '{"query":"driving home after a win"}' \
| jq -r '.takes[] | "\(.text)\t\(.songs.title)\t\(.songs.artist)"' # read column 1 aloud, keep 2 and 3
doorsneeds · nothing
classrooms & language
real sentences by real people, short enough to study.7
54
the esl warm-up
classrooms & language
students paraphrase three real lines on one record. the teacher files the best paraphrase under the student’s handle with the class key.
how
curl -s "https://photoprompting.org/api/v1/takes?song=75IQVo8hqI1iwVZyvkN2VT" | jq -r '.takes[:3][] | .text'
# then the winner: POST https://photoprompting.org/api/v1/takes {"spotifyId":"…","text":"…","author":"<student handle>"}
doorsneeds · a class key
55
the figure hunt
classrooms & language
fifty lines across ten records. students tag each as metaphor, simile, understatement or plain statement, then argue about the ones that are two at once.
how
for id in $TEN; do curl -s "https://photoprompting.org/api/v1/takes?song=$id" | jq -r '.takes[:5][] | .text'; done > lines.txt
doorsneeds · nothing
56
the tone lesson
classrooms & language
the voice profile’s moves and avoids, read as a lesson on deadpan: what the voice does, what it refuses to do, and why the refusals are the style.
a mood in; records you have not heard out, ranked by votes, each with its spotify link; the thirty seconds play in spotify’s player while you decide which to keep.
one record a week. members file their lines by sunday; the song’s page is the agenda for the call. nobody prepares a speech.
how
POST https://photoprompting.org/api/v1/takes {"spotifyId":"<this week>","text":"<one line>","author":"<member>"}
# the agenda: https://photoprompting.org/s/<this week>
doorsneeds · a key each
66
the band’s diary
discovery & djs
an artist’s page of lines, per song, lives at /diary/<artist>. pick the song with the fewest lines for next week; that is where the club is needed.
how
https://photoprompting.org/diary/men-i-trust
# or count per record: curl -s "https://photoprompting.org/api/v1/takes?song=<id>" | jq .count
doorsneeds · nothing
67
the mood bridge
discovery & djs
the mood gives you spotify links; you build the playlist in your own account from them. the wall picks, spotify plays, as it should be.
the hermit as a tool; the human as the only author.8
69
the assistant that credits
assistants & agents
connect once. ask what the wall says about a song; the line arrives with the writer’s handle and its permanent /t/ address, quoted exactly.
how
claude mcp add --transport http photoprompting https://photoprompting.org/api/mcp
# then: "what does the wall say about show me how by men i trust?"
doorsneeds · claude, chatgpt, cursor or claude code
70
the voice-mode dj
assistants & agents
in a spoken conversation the assistant uses speak_take: one sentence, the record, the credit, the line. it never reads an address aloud; it offers to send it.
how
speak_take(song) → "on show me how by men i trust, <credit> wrote: <the line>."
doorsneeds · an assistant with voice mode
71
the playlist reader
assistants & agents
paste twelve links. read_playlist answers one line per track, human first, and a grid card url you can post as is.
how
read_playlist(tracks: [<up to 12 links or ids>], name: "friday") → { lines, quota, grid_card_url }
doorsneeds · an assistant with the mcp server
72
the dictation filer
assistants & agents
"file this on it: …" — the assistant files your words under your handle and never its own. an identical line already on the record comes back as already there.
how
file_take(song, "this bass line walks home the long way", part: "sound")
→ { filed, url, line, on, song, card_url } or { already_on_the_wall: true }
doorsneeds · an assistant with the mcp server
73
the cursor epigraph
assistants & agents
the coding agent can fetch the line for your readme’s epigraph when you ask, credited. it cannot write one, and it says so.
a scheduled agent reads the day’s song and posts the credited line to your channel. it reads; it never files. the human is the only author on the wall.
how
daily 08:00 → GET https://photoprompting.org/api/daily-song → GET https://photoprompting.org/api/hermit/read?song=<spotifyId>&community=1 → post line + credit + /t/ address
doorsneeds · a scheduled agent
76
the bearer key
assistants & agents
an agent that already holds a key skips the sign-in screen: the key rides in the authorization header and reaches every door as x-api-key.
the bot in your server: drop a spotify link in any channel and the line comes back with the 30 seconds. /take files one; nobody leaves discord.
how
https://photoprompting.org/bot → add to server → paste a link in a channel
doorsneeds · a discord server
family & memory
one record, many hands, a year.6
85
the memorial record
family & memory
one song for someone gone. family files lines across a year, each under their own name; the song’s page is the memorial. the wall is public — write what you would say aloud.
how
POST https://photoprompting.org/api/v1/takes {"spotifyId":"<their song>","text":"<one line>","author":"<your name>"}
# the page: https://photoprompting.org/s/<their song>
doorsneeds · a key
86
the milestone book
family & memory
the song that was playing at each first — first steps, first word, first day — and one line filed on it. at the end of the year, pull them all.
how
for id in $MILESTONE_SONGS; do curl -s "https://photoprompting.org/api/v1/takes?song=$id" | jq -r '.takes[] | select(.author=="us") | "\(.created_at[:10]) \(.text)"'; done
doorsneeds · a key
87
the long-distance line
family & memory
two cities, one record a week. both file one line; the difference between the two lines is the phone call.
type how you feel in plain words; read what other people wrote when they felt it; play one record in spotify’s player. no advice, no account, no record kept of you.
the take you filed on your song, printed as the card. the record, your line, your name, the address of the wall it hangs on.
how
curl -s "https://photoprompting.org/api/hermit/card?take=<your take id>&format=card" -o card.png && lp card.png
doorsneeds · a printer
90
the family key
family & memory
one key, everyone files by voice through the assistant under their own handle. sunday, the week’s records go through the playlist door and the grid card goes on the fridge.
provenance, versions, quotas, the number on the balance.6
91
the element census
measure & meter
across a hundred records, which part of the song do people write about — the sound, the lyrics, the title, the art, the canvas. count the element field and find out.
how
for id in $HUNDRED; do curl -s "https://photoprompting.org/api/v1/takes?song=$id" | jq -r '.takes[].element // "unstamped"'; done | sort | uniq -c | sort -rn
doorsneeds · nothing
92
the drift log
measure & meter
the voice re-distills every monday. a weekly fetch of the profile, diffed against last week’s, is a log of how the wall’s voice moves.
a pipeline that pins voice_version. when the voice re-distills, the door answers 409 with the current version before anything is charged. nothing drifts in silence.
how
curl -s -X POST https://photoprompting.org/api/v1/voice -H "x-api-key: $PP_KEY" -H 'content-type: application/json' \
-d '{"prompt":"a folding chair","form":"review","voice_version":"hermit-<pinned>"}'
# 409 → { current_version } ; every 200 carries voice.version and corpus_takes
doorsneeds · a key
95
the provenance ledger
measure & meter
every licensed answer carries voice.version, corpus_takes and the license summary. write them beside the output you publish, and an audit a year from now knows exactly which voice wrote what.
a starter key from the till, bought signed in so the key is tied to your account. credits carry past the daily quota, and the balance page shows the number that is left.
how
https://photoprompting.org/api/till/go?sku=key-starter # signed in → the key is yours
https://photoprompting.org/api/till/go?sku=credits-100
curl -s https://photoprompting.org/api/credits/balance --cookie "<your session>" # or just open https://photoprompting.org/credits
doorsneeds · a card
the full house
four that walk most of the doors in one flow. their free parts were run against production, as written, before they were filed.4
97
the quarter-past canary
the full house
a quarter past midnight utc, when the record and the battle roll, a cron walks nine doors and asserts each against its documented shape: the date moved, both battle lines are present and end tomorrow, the profile is distilled and no older than eight days, a filed read answers 200 inside two seconds, three community records come back all human with zero fresh reads, the card header says 200, the oembed is rich, the qr is an svg, spotify’s player answers for the record. one line per door; a fail pages the channel with the door names in the message. every check is free by construction, so it can run every night for years.
how
# cron: 15 0 * * * (a quarter past midnight utc: did the house turn over?)
X=75IQVo8hqI1iwVZyvkN2VT; T=f9fdc3d9; D=$(date -u +%F); F=
ok(){ printf 'ok %s\n' "$1"; }; no(){ printf 'fail %s\n' "$1"; F="$F $1"; }
curl -s https://photoprompting.org/api/daily-song | jq -e --arg d "$D" '.date==$d and (.song.spotifyId|length)==22' >/dev/null && ok daily || no daily
curl -s https://photoprompting.org/api/battles/today | jq -e --arg d "$D" '.battle | (.take_a.text|length)>0 and (.take_b.text|length)>0 and .ends_at[:10] > $d' >/dev/null && ok battle || no battle
# eight days, not seven: on mondays this call is what triggers the re-distill, and it is served last week's profile with refreshing:true while that runs. by tuesday a stale one is a real fail
curl -s https://photoprompting.org/api/hermit/voice-profile | jq -e '.source=="distilled" and ((.week_epoch[:19]+"Z"|fromdate) > (now-8*86400))' >/dev/null && ok profile || no profile
[ -n "$(curl -sf -m 2 "https://photoprompting.org/api/hermit/read?song=$X&community=1&format=text")" ] && ok read || no read # -f, because the text format carries error messages as text: a 4xx or 5xx must print nothing
curl -s "https://photoprompting.org/api/hermit/playlist?ids=$X,0bu0to8kgrXDzWklgZ3BuQ,5vkkhwtKWnGkFu3tagE71D&community=1" | jq -e '.shown==3 and all(.tracks[]; .source=="community") and .quota.fresh_used==0' >/dev/null && ok playlist || no playlist
curl -sI "https://photoprompting.org/api/hermit/card?take=$T&format=card" | grep -qi '^x-card-status: 200' && ok card || no card
curl -s "https://photoprompting.org/api/oembed?url=https://photoprompting.org/t/$T&format=json" | jq -e '.type=="rich" and (.author_name|length)>0' >/dev/null && ok oembed || no oembed
curl -sI "https://photoprompting.org/api/qr?text=https://photoprompting.org/s/$X" | grep -qi 'content-type: image/svg' && ok qr || no qr
[ "$(curl -s -o /dev/null -w '%{http_code}' "https://open.spotify.com/embed/track/$X")" = 200 ] && ok player || no player
[ -z "$F" ] || curl -s -X POST "$SLACK_WEBHOOK" -H 'content-type: application/json' -d "$(jq -cn --arg t "the house did not turn over cleanly at midnight. failed:$F" '{text:$t}')"
doorsneeds · nothing; a cron and a webhook. every check is free
98
the night ledger
the full house
ten minutes before the day rolls at midnight utc, a cron writes the day down: the record, every line on it with votes and elements, the house line, the battle as it stood, the paperwork on the read (source, provenance, quality, cached or not), the story card and a qr to the page. one folder per date, named by the wall's own date so folder and record never disagree, and on sundays the week's seven records as a grid from filed lines only. a year of nights is an archive with every line still credited; it is not a corpus, and training on takes needs a license.
how
# crontab (must run in utc): CRON_TZ=UTC
# 50 23 * * * bash ~/ledger/night.sh (ten minutes before the record rolls)
cd ~/ledger; S=$(curl -s https://photoprompting.org/api/daily-song); D=$(jq -r .date <<<"$S"); ID=$(jq -r .song.spotifyId <<<"$S") # the folder takes the wall's date, not the host's
mkdir -p "$D"; cd "$D"; printf '%s\n' "$S" > song.json
# the interpret first, so the house line it leaves is the one the record carries. without ?fresh=1 nothing is metered: on a cache miss the house generates once and caches it for a day
curl -s -X POST https://photoprompting.org/api/hermit/interpret -H 'content-type: application/json' -d "{\"spotifyId\":\"$ID\"}" | jq '{source, quality, cached: (.cached // false), provenance, model}' > paperwork.json
curl -s "https://photoprompting.org/api/record/$ID" > record.json # every line with votes and elements; the house line under .house.line
curl -s https://photoprompting.org/api/battles/today | jq '.battle | {id, song: .song.title, a: {id: .take_a.id, text: .take_a.text, by: (.take_a.display_name // "anonymous"), votes: .votes_a}, b: {id: .take_b.id, text: .take_b.text, by: (.take_b.display_name // "anonymous"), votes: .votes_b}, leader: (if .votes_a == .votes_b then "tied" elif .votes_a > .votes_b then .take_a.id else .take_b.id end)}' > battle.json
TOP=$(jq -r '.topTake.id // empty' song.json); Q="song=$ID&community=1"; [ -n "$TOP" ] && Q="take=$TOP"
curl -s -D - "https://photoprompting.org/api/hermit/card?$Q&format=story" -o card.png | grep -i x-card-status # anything but 200 flags the folder
curl -s "https://photoprompting.org/api/qr?text=https://photoprompting.org/s/$ID" -o qr.svg
grep -qx "$ID" ../week.txt 2>/dev/null || echo "$ID" >> ../week.txt
# sundays: the week's records as the grid, filed lines only. &community=1 never generates; a record with nothing filed draws with no line
[ "$(date -u +%u)" = 7 ] && curl -s "https://photoprompting.org/api/hermit/playlist?ids=$(tail -7 ../week.txt | paste -sd, -)&community=1&name=week+of+$D&format=card" -o "../week-$D.png"
doorsneeds · a cron and a disk. nothing is metered
99
the audit of one record
the full house
one record, fully accounted for. the list gives every line with its writer, votes and element; the record gives the house line, its date and the sibling count; the interpret door says which line it serves and shows the paperwork; the profile says which week's judge is sitting, and every score below carries it; the audition scores the six most-voted lines, which is exactly one ten-minute window; the licensed voice writes the abstract for one unit of the key's daily quota, version stamped in the footnote. it ends as a printed card, and every number on the page came out of a door.
how
ID=75IQVo8hqI1iwVZyvkN2VT; curl -s "https://photoprompting.org/api/v1/takes?song=$ID" > list.json # the list: every line, credited, with votes and element
jq '{count, votes: ([.takes[].votes]|add), elements: ([.takes[] | (.element // "unstamped")]|group_by(.)|map({(.[0]): length})|add)}' list.json
curl -s "https://photoprompting.org/api/record/$ID" | jq '{title: .song.title, artist: .song.artist, house: .house.line, house_at: .house.updated_at, siblings: (.siblings|length)}'
curl -s -X POST https://photoprompting.org/api/hermit/interpret -H 'content-type: application/json' -d "{\"spotifyId\":\"$ID\"}" | jq '{line: .interpretation, source, quality, cached: (.cached // false), provenance}' # which line the door serves, and its paperwork; unmetered without ?fresh=1
WEEK=$(curl -s https://photoprompting.org/api/hermit/voice-profile | jq -r .week_epoch); T=$(jq -r .song.title list.json); A=$(jq -r .song.artist list.json) # which week's judge is sitting
# the audition: the six most-voted lines (six per address per ten minutes is the wall, so the loop stops there), each score stamped with the judge's week
jq -r '.takes | sort_by(-.votes)[:6][] | "\(.short)\t\(.text)"' list.json | while IFS=$'\t' read -r s text; do
curl -s -X POST https://photoprompting.org/api/hermit/audition -H 'content-type: application/json' -d "$(jq -cn --arg t "$text" --arg st "$T" --arg sa "$A" '{text:$t, songTitle:$st, songArtist:$sa}')" | jq -c --arg s "$s" --arg w "$WEEK" '{short:$s, score, tell, judge:$w}'
done > scores.jsonl
jq -s '[.[].score | numbers] | {scored: length, mean: (if length>0 then add/length else null end), min: min, max: max}' scores.jsonl # a 429 row has no score and is left out
# the abstract: licensed and stamped; one unit from the key's daily quota, then a credit. the prompt is a title, a name and two counts, nothing from the song
N=$(jq .count list.json); MEAN=$(jq -s -r '[.[].score | numbers] | if length>0 then add/length|round else "none" end' scores.jsonl)
curl -s -X POST https://photoprompting.org/api/v1/voice -H "x-api-key: $PP_KEY" -H 'content-type: application/json' -d "$(jq -cn --arg p "an audit of the wall on $T by $A: $N lines, mean audition $MEAN" '{prompt:$p, form:"micro-essay", length:"short"}')" | jq '{abstract: .text, version: .voice.version, corpus: .voice.corpus_takes, remaining: .usage.remaining}'
TOP=$(jq -r '.takes | sort_by(-.votes)[0].short' list.json); curl -s "https://photoprompting.org/api/hermit/card?take=$TOP&format=card" -o audit-front.png && lp audit-front.png # the print: the most-voted line, credited by the card itself
doorsneeds · a key and jq; one unit of the key's quota for the abstract
100
the artist issue
the full house
a zine about one artist, assembled from the wall. the record door names the seed and its six most-written sibling records with how many lines each carries (the rest live in the diary, where the back cover points); the list pulls every line on those seven, credited or anonymous, and tallies which part of the song this artist's listeners write about; the playlist door makes the cover from filed lines only, and its quota field is the proof that nothing on the cover was generated. the editorial is the one generated page, from the licensed voice for one unit of the key's daily quota, its version in the colophon; the embeds are the web edition, one per record.
how
# as one file: curl -sO https://photoprompting.org/examples/artist-issue.sh && bash artist-issue.sh "<spotify track link>" (PP_KEY=pp_… for the editorial)
# or by hand, door by door:
SEED=75IQVo8hqI1iwVZyvkN2VT; curl -s "https://photoprompting.org/api/record/$SEED" > seed.json; ARTIST=$(jq -r .song.artist seed.json)
jq -r '.siblings[] | "\(.count)\t\(.title)"' seed.json # the six most-written sibling records, with how many lines each; the rest are in the diary
jq -r '[.song.spotify_id] + [.siblings[].spotify_id] | .[]' seed.json > ids.txt
for id in $(cat ids.txt); do curl -s "https://photoprompting.org/api/v1/takes?song=$id" | jq -c '.song.title as $t | .takes[] | {title:$t, short, text, author: (.author // "anonymous"), element, votes}'; done > issue.jsonl # the list: every line on the seven, credited
jq -s 'group_by(.element // "unstamped") | map({(.[0].element // "unstamped"): length}) | add' issue.jsonl # which part of the song this artist's listeners write about
# the cover: one filed line per record as the grid. &community=1 never generates, so fresh_used reads 0 and every line on the cover is human
curl -s "https://photoprompting.org/api/hermit/playlist?ids=$(paste -sd, ids.txt)&community=1&name=the+artist+issue&format=card" -o cover.png
curl -s "https://photoprompting.org/api/hermit/playlist?ids=$(paste -sd, ids.txt)&community=1" | jq '{total, shown, fresh: .quota.fresh_used}'
jq -s -r 'group_by(.title)[] | sort_by(-.votes)[0].short' issue.jsonl | while read -r s; do curl -s "https://photoprompting.org/api/hermit/card?take=$s&format=square" -o "spread-$s.png"; done # the spreads: each record's most-voted line, credited by the card
# the editorial: the one generated page, licensed, one unit from the key's daily quota (then a credit), its version in the colophon. the prompt is a name and two counts, nothing from the songs
N=$(jq -s length issue.jsonl); R=$(wc -l < ids.txt | tr -d ' ')
curl -s -X POST https://photoprompting.org/api/v1/voice -H "x-api-key: $PP_KEY" -H 'content-type: application/json' -d "$(jq -cn --arg p "an issue about the wall on $ARTIST: $N lines across $R records" '{prompt:$p, form:"launch-post", length:"short"}')" | jq '{editorial: .text, version: .voice.version, corpus: .voice.corpus_takes}'
SLUG=$(printf '%s' "$ARTIST" | tr '[:upper:]' '[:lower:]' | sed "s/['\"]//g; s/[^a-z0-9]\{1,\}/-/g; s/^-//; s/-\$//"); curl -s "https://photoprompting.org/api/qr?text=https://photoprompting.org/diary/$SLUG" -o back.svg # the back cover: the diary, slugged the diary's way for plain names
for id in $(cat ids.txt); do echo "<script src=\"https://photoprompting.org/embed.js\" data-pp-embed=\"$id\" data-theme=\"dark\" async></script>"; done > web.html # the web edition: one embed per record
doorsneeds · a key and a printer; one unit of the key's quota for the editorial
the pattern under all one hundred: read (the line, with the 30 seconds), list (every line on a record), file (your line, with a key). anything that can hold those three is the hundred and first. the terms for what you may do with the output are on the voice license; the doors themselves are on the docs.