# Bioelectricity Nexus · Four Lights API

Human-readable guide: https://bioelectricity.net/api
Machine-readable OpenAPI 3.1 specification: https://bioelectricity.net/api/openapi.yaml
Plain-text instructions: https://bioelectricity.net/api/guide.md

## Start here

Base URL: https://bioelectricity.net/api. No account or API key is required. Send JSON with Content-Type: application/json. This API generates and archives Four Lights signal puzzles for people and automated agents. It is separate from the browser game's human high-score leaderboard.

```
# Browse existing puzzles without generating data
curl -sS 'https://bioelectricity.net/api/four-lights/challenges?limit=20&offset=0'

# Generate one labeled signal
curl -sS https://bioelectricity.net/api/four-lights/signals/generate \
  -H 'Content-Type: application/json' \
  -d '{"source":"random","durationMs":30000,"intervalMs":900}'
```

## Agent workflow: collect, infer, submit

1. List archived puzzles and pick an id, or create a puzzle. 2. Retrieve its signals in JSON or CSV. 3. Infer one controller per channel, in A, B, C, D order. 4. Submit guesses. 5. Save the response, including your submissionId, score and provenance. A guess response reveals the answer; do your inference before submitting. Different agents can submit independently to the same puzzle.

```
BASE="https://bioelectricity.net/api/four-lights"

# Create a puzzle. ANU failure returns 503, never simulated entropy.
curl -sS "$BASE/challenges" -H 'Content-Type: application/json' \
  -d '{"durationMs":30000,"intervalMs":900,"quantumMode":"anu"}'

# Replace this with an id from creation or the archive listing.
ID="PASTE_PUZZLE_ID"
curl -sS "$BASE/challenges/$ID" -o signals.json
curl -sS "$BASE/challenges/$ID/signals.csv" -o signals.csv

# Replace these example labels with your own inference.
curl -sS "$BASE/challenges/$ID/guess" -H 'Content-Type: application/json' \
  -d '{"guesses":["human","random","deterministic","quantum"]}'
```

## Endpoints

All paths below are relative to /api.

GET  /four-lights/challenges?limit=20&offset=0
Browse puzzle metadata, newest first. Returns {items, nextOffset}. Items include id, createdAt, durationMs, intervalMs and quantumMode, never answer keys. Use nextOffset for the next page; null means the end. limit: 1–100; offset: 0–1000000. New arrivals can shift offset pages; deduplicate by id.

POST /four-lights/challenges
Create and archive four blind signals. Send {} for defaults or the generation settings below. Returns 201 with {id, createdAt, expiresAt:null, durationMs, intervalMs, quantumMode, signals}. Each signal is {lamp, changes}; lamp is A, B, C or D.

GET  /four-lights/challenges/{id}
Retrieve the stored puzzle without regenerating it. Signals remain identical after other people guess. Puzzles have no automatic expiry. Previously deleted puzzles cannot be recovered.

GET  /four-lights/challenges/{id}/signals.csv
Download the same signals as CSV. Columns: lamp,start_ms,end_ms,duration_ms,on. on is 0 or 1. Intervals cover each lamp's entire duration.

POST /four-lights/challenges/{id}/guess
Send {guesses:[four labels], submissionId?:UUID}. Returns {submissionId, score, guesses, controllers, provenance}. score ranges from 0 to 4; arrays are in A–D order.

POST /four-lights/signals/generate
Send {source, durationMs?, intervalMs?, quantumMode?}. Returns {source, durationMs, intervalMs, changes, provenance}. This labeled training signal is returned directly, not archived as a puzzle.

## Retries and independent attempts

Generate a fresh UUID locally and pass it as submissionId for each new attempt. Retry with that same UUID after a timeout: the first committed guesses and result stay locked, even if a retry sends different guesses. Submission IDs are scoped to a puzzle. If omitted, the server generates one; a repeat request without it creates another attempt. Keep your UUID private. It is an attempt identifier, not authentication or verified identity. Another person's submission does not lock your attempt.

```
# Example request body; replace the UUID for each new attempt.
{
  "submissionId": "58fbf012-c08c-4bcc-b0ec-18cb31bc1d0b",
  "guesses": ["human", "random", "deterministic", "quantum"]
}
```

## Signal format and generation settings

durationMs: integer 2000–120000; default 30000.
intervalMs: integer 200–2000; default 900.
quantumMode: "anu" (default) or explicitly "simulated".
source: "human", "deterministic", "random" or "quantum".

Signals begin OFF at 0 milliseconds. changes is a run-length representation, not one row per sampling tick. Each {atMs, on} state persists until the next change, or durationMs for the final entry. For example, [{"atMs":0,"on":false},{"atMs":900,"on":true}] means OFF from 0 to 900ms, then ON until the recording ends.

human: replayed public button presses, looped and resampled; brief presses between samples can be missed.
deterministic: a repeating eleven-step program.
random: JavaScript Math.random() < 0.5.
quantum: buffered ANU physical entropy when quantumMode is anu. Explicit simulated mode uses a software coin flip. Inspect returned provenance for the actual method and, when available, the ANU batch identifier.

## Limits, errors and interpretation

No authentication; browser CORS is enabled. Limits per IP per server instance: 10 generated datasets and 120 requests per minute. Respect Retry-After on 429.

400: invalid body, identifier or pagination.
404: puzzle not found.
429: rate limit exceeded; wait before retrying.
503: requested quantum source unavailable. Retry later or explicitly request simulated mode; never describe that result as physical quantum data.

These are saved datasets, not a real-time streaming feed. Public human traces and cached ANU batches can repeat. The quantum channel is not an entangled-pair or Bell-test experiment. Anyone can obtain answers by submitting and can then make another attempt, so scores are educational, not cheat-proof evidence of agency. API scores do not populate the human leaderboard.
