API Reference
Every app on davenn.com is served by a single api.php. Endpoints are selected by an action query parameter and always return JSON.
74 endpoints Β· 31 tables
Nothing matches that.
Meeting Cost Timer
Answers with the number of rows removed, so the caller can tell a cleared week from a week_key that matched nothing.
Authentication
None
Takes
&week_key=YYYY-MM-DD β drop one week's meetings.
Query parameters
| week_key |
Title, cost and length must all be present and positive. A meeting with no cost is a timer that was never really started, and keeping it only adds noise to the week.
Authentication
None
Takes
body: { title, cost, seconds, week_key }
Cost is the entire point of the app, so the ranking leads with it.
Authentication
None
Takes
&week_key=YYYY-MM-DD β one week's meetings, dearest first.
Query parameters
| week_key |
Track Timer
Not scoped to a week or a user: it empties the table.
Authentication
None
Takes
remove every saved session.
Authentication
None
Takes
&id=X β remove a single session.
Query parameters
| id |
Athletes are stored as given β the app owns their shape, not the database.
Authentication
None
Takes
body: { name, duration, athletes[] }
Athletes ride along as a JSON blob and are decoded on the way out: the roster changes every session, so there is nothing stable to make columns of.
Authentication
None
Takes
every saved session, newest first.
Toolshare
Authentication
Toolshare account β X-Auth-Token
Takes
body: { invite_code }
Authentication
Toolshare account β X-Auth-Token
Takes
(multipart for photo upload OR JSON)
Authentication
Toolshare account β X-Auth-Token
Takes
&id=X
Query parameters
| id |
Multipart, owner only. A new photo replaces the old one and the old file is deleted; sending no photo keeps the current one.
Authentication
Toolshare account β X-Auth-Token
Takes
&id=X
Query parameters
| id |
each of them has. Friendships are stored undirected, so the query checks both columns and takes whichever side is not you.
Authentication
Toolshare account β X-Auth-Token
Takes
everyone this user shares with, and how many tools
Authentication
Toolshare account β X-Auth-Token
Takes
vision-based auto-fill via Claude
Authentication
None
Takes
&code=xxx (no auth required)
Query parameters
| code |
Authentication
None
Takes
body: { username, password }
Authentication
Toolshare account β X-Auth-Token
Takes
ends only this device's session, other devices stay signed in
Codes are created lazily, so an account that never shares anything never carries one.
Authentication
Toolshare account β X-Auth-Token
Takes
this user's invite code, minted on first request.
Authentication
Toolshare account β X-Auth-Token
Takes
current user's tools only
Signs the new account straight in and returns its token β registering and then being asked to log in is a step with no purpose. A taken username is the only way the insert can fail, so that is what the conflict reports.
Authentication
None
Takes
body: { username, display_name, email, password }
Authentication
Toolshare account β X-Auth-Token
Takes
&id=X
Query parameters
| id |
Only friends of the owner may ask. A stranger's tool answers exactly like a missing one, so sequential ids cannot be walked to email every owner.
Authentication
Toolshare account β X-Auth-Token
Takes
create borrow request + email owner
Authentication
Toolshare account β X-Auth-Token
Takes
badge count of pending incoming requests
Authentication
Toolshare account β X-Auth-Token
Takes
inbox (owner) + outbox (requester) for current user
Authentication
Toolshare account β X-Auth-Token
Takes
&id=X body: { status: "approved"|"declined" }
Query parameters
| id |
Authentication
Toolshare account β X-Auth-Token
Takes
&id=X (request id) β mark tool as returned
Query parameters
| id |
Authentication
Toolshare account β X-Auth-Token
Takes
unique tags from visible tools
Authentication
Toolshare account β X-Auth-Token
Takes
friends-gated community view
Flight Tracker
body: { from_code, to_code, from_city, to_city, flight_date, airline?, seat_class?, flight_number? } Airport codes are upper-cased on the way in, and a flight that lands where it started is refused rather than stored as a curiosity.
Authentication
Flight Tracker account β X-Auth-Token
Rows that fail validation are skipped instead of failing the batch, and the reply counts what landed. An import of fifty flights is still worth keeping when two lines are malformed, and the caller can see the shortfall.
Authentication
Flight Tracker account β X-Auth-Token
Takes
body: { flights: [ β¦ ] } β bulk import.
Ownership is part of the WHERE clause, so a guessed id belonging to someone else matches nothing and answers 404 rather than deleting their row.
Authentication
Flight Tracker account β X-Auth-Token
Takes
&id=X β remove one of this user's flights.
Query parameters
| id |
Authentication
Flight Tracker account β X-Auth-Token
Takes
this user's flights, most recent first.
Issues a fresh token over the top of the old one, so signing in here signs out whichever device was signed in before.
Authentication
None
Takes
body: { username, password }
Authentication
Flight Tracker account β X-Auth-Token
Takes
clears the stored token.
The token lives on the user row rather than in a sessions table, unlike Toolshare β one signed-in device at a time is all this app has needed.
Authentication
None
Takes
body: { username, display_name, password }
Glucose
This is the shape the streak calendar and time-in-range bars want; the aggregation happens in SQL so a year of history stays a small response.
Authentication
Glucose read token β X-BG-Token or ?token=
Takes
&token=β¦&days=30&low=70&high=180 β one row per local day.
Query parameters
| days |
| low |
| high |
mgdl 0 means nothing is stored yet. Trend follows Dexcom's own ordering, so a device can index an arrow glyph straight off it: 0 unknown Β· 1 up-up Β· 2 up Β· 3 up-45 Β· 4 flat Β· 5 down-45 Β· 6 down Β· 7 down-down
Authentication
Glucose read token β X-BG-Token or ?token=
Query parameters
| low |
| high |
| spark |
Authentication
Glucose read token β X-BG-Token or ?token=
Takes
&id=β¦&token=β¦
Query parameters
| id |
body: { id?, start_at, end_at?, tag, note? } Creates, or updates when id is given. Returns the stored row.
Authentication
Glucose read token β X-BG-Token or ?token=
Takes
&token=β¦
Read token: anything that may read the readings may read what explains them.
Authentication
Glucose read token β X-BG-Token or ?token=
Takes
&token=β¦&hours=24 β annotations overlapping the window.
Query parameters
| hours |
Authentication
Glucose read token β X-BG-Token or ?token=
Takes
&token=β¦&hours=24&low=70&high=180 β raw readings + summary.
Query parameters
| hours |
| low |
| high |
minutes_ago is what a display should use to decide it has gone stale: show the age, and never present an old number as though it were current.
Authentication
Glucose read token β X-BG-Token or ?token=
Takes
&token=β¦ β the newest stored reading.
Asks the poller to pull from Dexcom now, so a manual refresh reflects live data rather than whatever was last stored. The admin secret stays here on the server β the page only ever holds the read-only token, which is why this is a proxy rather than the browser calling the poller directly.
Authentication
Glucose read token β X-BG-Token or ?token=
Takes
&token=β¦
Confidence Pool
Authentication
None
Takes
&id=X
Query parameters
| id |
Authentication
Single-use link token β ?token=
Takes
&token=β¦ β drop a staged sheet once it is saved.
Query parameters
| token |
Authentication
Single-use link token β ?token=
Takes
&token=β¦ β collect a staged sheet for review.
Query parameters
| token |
Authentication
None
Takes
{season, week, player_name, picks:[{game_id,pick,confidence}]}
Creates the week, or updates the spreads on one that already has entries.
Authentication
None
Takes
{season, week, push_rule, games:[{away,home,favorite,spread}]}
Reads a filled-in sheet. Saves no picks β the app shows the result for correction first, because a misread confidence number costs more than a misread team name and both happen.
Authentication
None
Takes
(multipart: photo, optional season + week)
Query parameters
| season |
| week |
Authentication
None
Takes
Twilio inbound webhook.
Authentication
None
Takes
&season=&week=
Query parameters
| season |
| week |
Authentication
None
Takes
&season=&week=
Query parameters
| season |
| week |
| force |
Authentication
None
Takes
every week that has been set up, newest first.
WildcatsXC
Lines rows the app parsed itself β results pasted from a site such as MileSplit β up against the stored athletes, exactly as xc_scan does for a read sheet, so a paste gets the same known / new / "same as" tags. Saves nothing. The parsing stays in the browser: pasted text is regular enough that a model read would only add cost and minutes.
Authentication
WildcatsXC team PIN β X-XC-PIN
Takes
{rows:[{race, distance_m, place, name, grade, school, time}]}
results anywhere go with it.
Authentication
WildcatsXC team PIN β X-XC-PIN
Takes
&id=X β the meet and its results; runners left with no
Query parameters
| id |
xc_scan returns, so the app edits a saved meet with the grid it checks a scan in.
Authentication
WildcatsXC team PIN β X-XC-PIN
Takes
&id=X β one meet with every result, in the same row shape
Query parameters
| id |
holds and how many came from each school (schools: name β count), so the app can show just the meets the coach's own school ran. Also what the app calls to check a PIN when it is first entered.
Authentication
WildcatsXC team PIN β X-XC-PIN
Takes
every meet, newest first, with how many runners each
Imports one MileSplit meet's results from a link a coach pasted. Only ever runs because a person pasted a link and pressed Import β never on a schedule, never following links to other meets. Two requests: the results page (meet name, date, which results files exist) and the performances data that page itself loads. Files MileSplit marks as PRO-only are skipped, not fetched. The link's own event / gender / division filters choose the race; a link without them imports every race. Saves nothing β the rows come back in xc_scan's shape for checking.
Authentication
WildcatsXC team PIN β X-XC-PIN
Takes
{url}
date order, for the Team tab to arrange. school[]=A&school[]=B (up to 40) returns several at once, each row carrying its school β what the Conference and Section tabs ask for. Flat rather than pre-arranged: how rows are grouped (boys and girls, which distance, top ten per school) is a view choice that belongs in the browser.
Authentication
WildcatsXC team PIN β X-XC-PIN
Takes
&school=X β every result for one school, flat and in
Query parameters
| school |
With meet_id, the rows replace that meet's results β the edit path, where a renamed runner must not leave their old row behind. Without one, a meet with the same name and date is added to rather than duplicated, so a sheet's second page can be scanned later; a runner already in it is updated. A race under 5000 m is saved as 5K: each time becomes its xcTo5k() equivalent and the distance 5000, so a 3K or 2-mile meet lands on the same trend line. The original distance and times are not kept. Under 1000 m is refused as a typo (3 for 3000) rather than turned into an absurd 5K time.
Authentication
WildcatsXC team PIN β X-XC-PIN
Takes
{meet_id?, name, date, location, rows:[{race, distance_m, place, name, grade, school, time}]}
Reads a results PDF. PDFs only, by the owner's choice: results come in as a PDF, a results link (xc_milesplit) or pasted text (xc_match), never a photo. Saves nothing β the app shows every row for checking first. Rows come back flat, each carrying its race, and already lined up against the stored athletes: an exact name + school match takes the stored spelling, and a new name close to an existing teammate's carries that name as similar for the coach to accept or ignore. The files are never written to disk. With page (and a single PDF), reads just that page β how the app reads a PDF, a page per request, so no request runs long enough for the host to kill it. Every reply carries page_count.
Authentication
WildcatsXC team PIN β X-XC-PIN
Takes
(multipart: files[] β PDFs; optional schools, page)
counts, most results first. Feeds the team picker on the Team tab.
Authentication
WildcatsXC team PIN β X-XC-PIN
Takes
every school on record with its runner and result
Daily Tasks
Authentication
Toolshare account β X-Auth-Token
Takes
&date=YYYY-MM-DD β load this user's task list for a given day
Query parameters
| date |
Authentication
None
Takes
body: { name, task_count, total_seconds, week_key }
Authentication
Toolshare account β X-Auth-Token
Takes
body: { date, tasks, next_id } β upsert this user's task list for a given day
Ranked by tasks finished, then by time taken: doing more wins, and doing the same amount faster settles the tie.
Authentication
None
Takes
&week_key=YYYY-MM-DD β the week's leaderboard.
Query parameters
| week_key |
Face Breaker
Authentication
None
Takes
top 10 for the current ISO week (no auth)
Authentication
None
Takes
body: { name, score } β no auth required
Reaction Test
Authentication
None
Takes
&week_key=YYYY-MM-DD β reset one week.
Query parameters
| week_key |
Authentication
None
Takes
&week_key=YYYY-MM-DD β the week's ten fastest.
Query parameters
| week_key |
Only a top-ten time is kept. A slower one comes back as success:false with a 200, not an error β missing the board is an ordinary outcome of playing, and the app should be able to say so without dressing it up as a failure.
Authentication
None
Takes
body: { name, avg_ms, week_key }
Update Notifications
Removes the operator's own test phone from the list so sign-up can be tested from a clean slate. Takes no input and needs no secret, so it can be tapped from a phone browser. Safe to leave open only because it can touch exactly one row: the number comes from server configuration, never from the request, and the repo is public so it is not written here. With no test phone configured it does nothing.
Authentication
None
"website" is an invisible honeypot field β real users never fill it in.
Authentication
None
Takes
body: { contact_type: 'email'|'phone', contact_value, website? }
Authentication
Single-use link token β ?token=
Takes
&token=xxx β clicked from an email/SMS, so it renders HTML, not JSON
Query parameters
| token |
Analytics
The page-view beacon every page sends on load: path, referrer, device class, installed-as-app, theme and language. Open by necessity, and it always answers 204 so a caller cannot tell whether a hit was kept. The server adds browser and OS families and the city, which is looked up from the IP; the IP itself is never stored.
Authentication
None
Takes
body: { p, r, d, i, t, l }
Schema
All tables live in one database and are created on demand β the CREATE TABLE IF NOT EXISTS block at the top of api.php runs on every request. New tables appear automatically; column changes to an existing table need a manual ALTER against the live database.
Meeting Cost Timer
meetings
id INT AUTO_INCREMENT PRIMARY KEY
title VARCHAR(120) NOT NULL
cost DECIMAL(12,2) NOT NULL
seconds INT NOT NULL
week_key DATE NOT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
Track Timer
track_sessions
id INT AUTO_INCREMENT PRIMARY KEY
name VARCHAR(120) NOT NULL
duration BIGINT NOT NULL
athletes LONGTEXT NOT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
Toolshare
tb_users
id INT AUTO_INCREMENT PRIMARY KEY
username VARCHAR(60) NOT NULL UNIQUE
password_hash VARCHAR(255) NOT NULL
display_name VARCHAR(80) NOT NULL
email VARCHAR(120) NOT NULL
token VARCHAR(64) DEFAULT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
tb_tools
id INT AUTO_INCREMENT PRIMARY KEY
owner_id INT NOT NULL
name VARCHAR(120) NOT NULL
brand VARCHAR(80) DEFAULT NULL
category VARCHAR(60) DEFAULT NULL
notes TEXT DEFAULT NULL
photo_url VARCHAR(255) DEFAULT NULL
status ENUM('available','borrowed') DEFAULT 'available'
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
FOREIGN KEY (owner_id) REFERENCES tb_users(id) ON DELETE CASCADE
tb_requests
id INT AUTO_INCREMENT PRIMARY KEY
tool_id INT NOT NULL
requester_id INT NOT NULL
owner_id INT NOT NULL
status ENUM('pending','approved','declined','returned') DEFAULT 'pending'
message TEXT DEFAULT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
FOREIGN KEY (tool_id) REFERENCES tb_tools(id) ON DELETE CASCADE
FOREIGN KEY (requester_id) REFERENCES tb_users(id) ON DELETE CASCADE
FOREIGN KEY (owner_id) REFERENCES tb_users(id) ON DELETE CASCADE
tb_sessions
token VARCHAR(64) PRIMARY KEY
user_id INT NOT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
INDEX idx_user (user_id)
FOREIGN KEY (user_id) REFERENCES tb_users(id) ON DELETE CASCADE
tb_friendships
id INT AUTO_INCREMENT PRIMARY KEY
user_a INT NOT NULL
user_b INT NOT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
UNIQUE KEY unique_pair (user_a, user_b)
FOREIGN KEY (user_a) REFERENCES tb_users(id) ON DELETE CASCADE
FOREIGN KEY (user_b) REFERENCES tb_users(id) ON DELETE CASCADE
Flight Tracker
ft_users
id INT AUTO_INCREMENT PRIMARY KEY
username VARCHAR(60) NOT NULL UNIQUE
password_hash VARCHAR(255) NOT NULL
display_name VARCHAR(80) NOT NULL
token VARCHAR(64) DEFAULT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
ft_flights
id INT AUTO_INCREMENT PRIMARY KEY
user_id INT NOT NULL
from_code VARCHAR(4) NOT NULL
to_code VARCHAR(4) NOT NULL
from_city VARCHAR(100) NOT NULL
to_city VARCHAR(100) NOT NULL
flight_date DATE NOT NULL
airline VARCHAR(80) DEFAULT NULL
seat_class VARCHAR(30) DEFAULT NULL
flight_number VARCHAR(20) DEFAULT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
FOREIGN KEY (user_id) REFERENCES ft_users(id) ON DELETE CASCADE
Glucose
bg_readings
id INT AUTO_INCREMENT PRIMARY KEY
reading_at DATETIME NOT NULL
mgdl SMALLINT NOT NULL
trend VARCHAR(20) DEFAULT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
UNIQUE KEY uniq_reading_at (reading_at)
bg_events
id INT AUTO_INCREMENT PRIMARY KEY
start_at DATETIME NOT NULL
end_at DATETIME DEFAULT NULL
tag VARCHAR(32) NOT NULL
note VARCHAR(500) DEFAULT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
INDEX idx_start (start_at)
Confidence Pool
cp_weeks
id INT AUTO_INCREMENT PRIMARY KEY
season INT NOT NULL
week INT NOT NULL
push_rule ENUM('award','void') DEFAULT 'void'
scores_fetched_at DATETIME DEFAULT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
UNIQUE KEY uk_season_week (season, week)
cp_games
id INT AUTO_INCREMENT PRIMARY KEY
week_id INT NOT NULL
sort_order INT NOT NULL
away_team VARCHAR(8) NOT NULL
home_team VARCHAR(8) NOT NULL
favorite ENUM('home','away') NOT NULL
spread DECIMAL(4,1) NOT NULL
espn_id VARCHAR(24) DEFAULT NULL
away_score INT DEFAULT 0
home_score INT DEFAULT 0
state ENUM('pre','in','post') DEFAULT 'pre'
detail VARCHAR(60) DEFAULT NULL
kickoff DATETIME DEFAULT NULL
UNIQUE KEY uk_week_order (week_id, sort_order)
FOREIGN KEY (week_id) REFERENCES cp_weeks(id) ON DELETE CASCADE
cp_entries
id INT AUTO_INCREMENT PRIMARY KEY
week_id INT NOT NULL
player_name VARCHAR(60) NOT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
UNIQUE KEY uk_week_player (week_id, player_name)
FOREIGN KEY (week_id) REFERENCES cp_weeks(id) ON DELETE CASCADE
cp_picks
id INT AUTO_INCREMENT PRIMARY KEY
entry_id INT NOT NULL
game_id INT NOT NULL
pick ENUM('home','away') NOT NULL
confidence INT NOT NULL
UNIQUE KEY uk_entry_game (entry_id, game_id)
UNIQUE KEY uk_entry_conf (entry_id, confidence)
FOREIGN KEY (entry_id) REFERENCES cp_entries(id) ON DELETE CASCADE
FOREIGN KEY (game_id) REFERENCES cp_games(id) ON DELETE CASCADE
cp_players
id INT AUTO_INCREMENT PRIMARY KEY
phone VARCHAR(20) NOT NULL UNIQUE
player_name VARCHAR(60) NOT NULL
opted_out TINYINT(1) DEFAULT 0
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
cp_pending
id INT AUTO_INCREMENT PRIMARY KEY
token VARCHAR(40) NOT NULL UNIQUE
week_id INT NOT NULL
player_name VARCHAR(60) NOT NULL
phone VARCHAR(20) DEFAULT NULL
rows_json LONGTEXT NOT NULL
note VARCHAR(255) DEFAULT NULL
claimed_at DATETIME DEFAULT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
INDEX idx_week (week_id)
FOREIGN KEY (week_id) REFERENCES cp_weeks(id) ON DELETE CASCADE
WildcatsXC
xc_meets
id INT AUTO_INCREMENT PRIMARY KEY
name VARCHAR(160) NOT NULL
meet_date DATE NOT NULL
location VARCHAR(160) DEFAULT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
INDEX idx_date (meet_date)
xc_athletes
id INT AUTO_INCREMENT PRIMARY KEY
name VARCHAR(80) NOT NULL
school VARCHAR(120) NOT NULL
match_key VARCHAR(210) NOT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
UNIQUE KEY uniq_athlete (match_key)
xc_results
id INT AUTO_INCREMENT PRIMARY KEY
meet_id INT NOT NULL
athlete_id INT NOT NULL
race VARCHAR(80) NOT NULL
distance_m SMALLINT DEFAULT NULL
place SMALLINT DEFAULT NULL
grade TINYINT DEFAULT NULL
time_ms INT NOT NULL
UNIQUE KEY uniq_run (meet_id, athlete_id)
INDEX idx_athlete (athlete_id)
FOREIGN KEY (meet_id) REFERENCES xc_meets(id) ON DELETE CASCADE
FOREIGN KEY (athlete_id) REFERENCES xc_athletes(id) ON DELETE CASCADE
xc_pin_attempts
id INT AUTO_INCREMENT PRIMARY KEY
ip VARCHAR(45) NOT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
KEY idx_ip (ip, created_at)
Daily Tasks
dt_scores
id INT AUTO_INCREMENT PRIMARY KEY
name VARCHAR(60) NOT NULL
task_count INT NOT NULL
total_seconds INT NOT NULL
week_key DATE NOT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
INDEX idx_week (week_key, task_count, total_seconds)
dt_tasks
user_id INT NOT NULL
date_key DATE NOT NULL
tasks_json LONGTEXT NOT NULL
next_id INT NOT NULL DEFAULT 1
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
PRIMARY KEY (user_id, date_key)
FOREIGN KEY (user_id) REFERENCES tb_users(id) ON DELETE CASCADE
Face Breaker
fb_scores
id INT AUTO_INCREMENT PRIMARY KEY
name VARCHAR(32) NOT NULL
score INT NOT NULL
week_start DATE NOT NULL
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
INDEX idx_week_score (week_start, score)
Reaction Test
reaction_scores
id INT AUTO_INCREMENT PRIMARY KEY
name VARCHAR(60) NOT NULL
avg_ms INT NOT NULL
week_key DATE NOT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
INDEX idx_week (week_key, avg_ms)
Update Notifications
subscribers
id INT AUTO_INCREMENT PRIMARY KEY
contact_type ENUM('email','phone') NOT NULL
contact_value VARCHAR(120) NOT NULL
unsub_token VARCHAR(64) NOT NULL
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
UNIQUE KEY uniq_contact (contact_type, contact_value)
Analytics
an_hits
id BIGINT AUTO_INCREMENT PRIMARY KEY
ts DATETIME NOT NULL
page VARCHAR(60) NOT NULL
vid CHAR(16) NOT NULL
referrer VARCHAR(100) DEFAULT NULL
device VARCHAR(8) DEFAULT NULL
browser VARCHAR(16) DEFAULT NULL
os VARCHAR(16) DEFAULT NULL
installed TINYINT(1) NOT NULL DEFAULT 0
theme VARCHAR(5) DEFAULT NULL
lang VARCHAR(15) DEFAULT NULL
city VARCHAR(80) DEFAULT NULL
region VARCHAR(80) DEFAULT NULL
country CHAR(2) DEFAULT NULL
lat DECIMAL(5,2) DEFAULT NULL
lon DECIMAL(5,2) DEFAULT NULL
KEY idx_ts (ts)
KEY idx_vid (vid, ts)
an_daily
day DATE NOT NULL
page VARCHAR(60) NOT NULL
views INT NOT NULL
visitors INT NOT NULL
PRIMARY KEY (day, page)
an_salt
day DATE NOT NULL PRIMARY KEY
salt CHAR(64) NOT NULL
an_api
day DATE NOT NULL
method VARCHAR(7) NOT NULL
action VARCHAR(40) NOT NULL
calls INT NOT NULL DEFAULT 0
errors INT NOT NULL DEFAULT 0
total_ms BIGINT NOT NULL DEFAULT 0
max_ms INT NOT NULL DEFAULT 0
PRIMARY KEY (day, method, action)
Other
subscribe_attempts
id INT AUTO_INCREMENT PRIMARY KEY
ip VARCHAR(45) NOT NULL
contact_value VARCHAR(120) NOT NULL
texted TINYINT(1) NOT NULL DEFAULT 0
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
KEY idx_ip (ip, created_at)
KEY idx_contact (contact_value, created_at)
10 operator endpoints are not listed here.