Skip to main content

API Documentation

Auto-generated reference for all endpoints

559 endpoints across 49 categories

Returns merged branding config, assets, and phase completion for a site. Supports ETag caching with 5-minute Cache-Control.

Parameters

site_key

No auth required. CORS enabled. Returns JSON.

Optional query params: site — site key (e.g. 'clothing', 'bingo') title — override the site name tagline — override the tagline

Parameters

site e.g. clothing

Accepts JSON body: { necessary: 1, analytics: 0|1, marketing: 0|1 } Returns JSON: { success: true, id: N }

Receives Content-Security-Policy violation reports from browsers. Rate-limited to 30/min per IP hash. Logs to cli/logs/csp-violations.log. Standard browser payload: {"csp-report": {document-uri, violated-directive, ...}}

Returns: {degrees: [...], stats: {...}, progress: [...]}

Returns a chronological activity feed. If the user is logged in and follows other users, returns a personalized feed from those they follow. Otherwise returns the global feed. ?mode=following — force following-only feed (requires auth) ?mode=global — force global feed ?types=music,events,cards,achievements,members — filter by type ?limit=25 — items per page (max 100) ?offset=0 — pagination offset

Parameters

mode e.g. following

Returns a detailed JSON report on all critical services: database, session, environment, SMTP, OAuth providers, blockchain RPCs, filesystem, PHP runtime, and migrations. Access control (one must match): 1. X-Health-Key header matches HEALTH_CHECK_KEY env var 2. Logged-in admin user (is_admin = 1) 3. IP in .maintenance whitelist If none match, returns 403. Used by uptime monitors, admin dashboards, and deploy scripts.

Maintained for backward compatibility. New code should use /api/subscribe.php directly.

Generates a 1200x630 PNG with credential details using PHP GD. Caches images for 24 hours in /cache/og-images/.

Parameters

hash e.g. VERIFICATION_HASH

Returns cached social stats (Discord, Telegram, registered users). Data is pre-cached by cli/pulse-cache.php (cron job). No authentication required. Response: { "success": true, "data": { "discord_members": N, "discord_online": N, "telegram_members": N, "soundcloud_followers": N, "youtube_subscribers": N, "registered_users": N, "cached_at": "ISO8601", "stale": false } }

region=pdx|dmv|bars (default: all) type=music|etc (event_type filter) limit=N (default 60, max 200) featured=1 (only is_featured=1)

Parameters

q e.g. term
limit e.g. 8

GET /api/site-config.php?site_key=oregontires GET /api/site-config.php?site_key=tre5magic&fields=google_analytics_id,logo_url

Parameters

site_key e.g. oregontires

Cached for 1 hour via file-based cache. Respects the 50,000 URL sitemap limit.

Cache: 45s (client-side poll interval) Rate limit: 0 (public stats)

Body: { "email": "user@example.com", "source": "subscribe" } Creates email_subscribers record + pending user + sends invitation. If user already exists with a password, just adds them to subscribers. Replaces legacy api/newsletter-subscribe.php.

Generates a printer-friendly HTML page with auto print dialog. The user can "Print to PDF" from their browser.

Parameters

user e.g. USERNAME

Receives CLS/LCP/INP metrics from the browser via sendBeacon. Rate-limited to 60 requests/min per IP hash. Returns 204 on success.

Actions: {"action": "generate"} — Generate a new TOTP secret + QR URI {"action": "enable", "code": "123456"} — Verify code against pending secret, enable 2FA {"action": "disable", "code": "123456", "password": "..."} — Disable 2FA (requires TOTP + password)

Called during login when a user has 2FA enabled. Expects a pending 2FA session (set by login.php after password verification). Body: {"code": "123456"} or {"backup_code": "ABCD1234"} On success: completes login, sets session, returns JWT. On backup code use: marks that code as used (NULLs it out).

Called when a logged-in user (via wallet/OAuth) wants to add email login. Sets both email and password_hash on the user, then sends a verification email. The actual email connection in user_connections is created when the user clicks the verification link (handled by verify-email.php). Accepts: { email: string, password: string, confirm_password: string } Returns: { success: true, email_verification: 'pending', message: '...' }

No description available.

We exchange the Google code here (on hiphop.world, whose callback URL IS in Google's allow-list), federate the profile into the central users table, mint a target_origin-bound sso_token, and bounce the browser to the tenant's /sso endpoint for session seating. Assumes $pdo is already available from the parent include. Security hardening (2026-04-18): - HIGH #4: mint inserts both token (plain) AND token_hash = SHA-256(token) - HIGH #5: email-merge step guarded by email_verified check - CRITICAL #2: target_origin normalized (lowercase + strip trailing slash) on mint - MEDIUM #8: failure redirects after state lookup use $tenantDomain (not hiphop.world) - MEDIUM #9: tenant_domain re-resolved from tenant_federation_clients, not metadata - MEDIUM #12: raw OAuth response bodies never logged; only parsed error fields

No description available.

Validates the invitation token, sets the user's password, creates the email connection, and logs them in.

We exchange the LinkedIn code here (on hiphop.world, whose callback URL IS in LinkedIn's allow-list), federate the profile into the central users table, mint a target_origin-bound sso_token, and bounce the browser to the tenant's /sso endpoint for session seating. LinkedIn redirect URI registered in LinkedIn Console: https://hiphop.world/api/auth/linkedin-broker-callback.php Assumes $pdo is already available from the parent include.

No description available.

No description available.

No description available.

This endpoint is for LOGGED-OUT users who have lost access to their account but previously set a recovery wallet in the Security Hub. Flow: 1. User provides wallet_address, wallet_type, signature, nonce 2. Server verifies nonce + signature 3. Server looks up recovery_wallets table for matching wallet 4. If found: re-enable account (if disabled), create session, return redirect 5. Generic error for all failure types (do not leak which wallets are registered)

New flow (email-only): 1. User submits email (no password required) 2. Creates pending user (NULL password_hash) 3. Creates email_subscribers record 4. Sends invitation email with "Set Your Password" link 5. User does NOT get logged in immediately If the user already has an account with a password, they're told to sign in. If the user exists from OAuth/wallet with no password, they get an invitation.

Accepts { "email": "..." }. Always returns success to prevent user enumeration. Rate limits to 3 reset requests per email per hour.

For users who registered but haven't set their password yet. Body: { "email": "user@example.com" }

Requires authentication. Checks if the user has a pending (unverified) email verification. If yes, creates a new token and resends the email. Rate limited: only allows resend every 60 seconds.

Accepts { "token": "...", "password": "...", "confirm_password": "..." }. Verifies token, validates password constraints, updates the user's password.

Called when login.php has ?return= and user may already be logged in. If authenticated: generates SSO token → returns redirect URL (skip login form) If not: returns { authenticated: false }

No Node.js dependency — uses kornrunner/keccak + simplito/elliptic-php

Public endpoint — no authentication required. Response: {success: true, artist: {entity, music, media, groups, shows, related}}

Parameters

slug (required) artist slug

Public endpoint — no authentication required. Response: {success: true, artists: [...], total: N, limit: N, offset: N}

Parameters

region region slug (optional, filters by region)
sub_region sub-region slug (optional)
type artist_type filter (optional)
q search query (optional)
verified 0 or 1 (optional)
limit results per page (default 24, max 100)
offset pagination offset (default 0)

Public endpoint — no authentication required. Response: { success: true, artists: [...], total: N, page: N, per_page: N, filters: {countries: [...], languages: [...], genres: [...]} }

Parameters

country ISO country code (e.g. US, GB, FR)
language ISO language code (e.g. en, es, fr)
genre genre slug (e.g. trap, boom-bap, reggaeton)
region region slug (e.g. dmv, bars, pdx)
continent continent filter (north_america, europe, latin_america, africa, asia, oceania)
q search query (name, stage name, hometown)
sort name|popular|recent (default: popular)
page page number (default 1)
per_page results per page (default 24, max 100)

Request body: { entity_id: int } CSRF: required Response (success): { success: true, entity: {...} } Response (error): { success: false, error: "..." }

Public endpoint — no authentication required. Response: {success: true, events: [{..., lineup: [...]}], total: N, limit: N, offset: N}

Parameters

region region slug (optional, filters by region)
limit results per page (default 24, max 100)
offset pagination offset (default 0)

Public endpoint, rate limited. Response: {success: true, artists: [{entity_id, name, slug, artist_type, avatar_url, is_verified, region_short_name}]}

Parameters

q search query (required, min 2 characters)
limit max results (default 10, max 50)

GET ?event_id=123 — Public: get lineup for an event POST {event_id, artist_entity_id, billing_order?, set_time?, set_length_minutes?} — Auth: add artist to lineup (event creator only) DELETE {event_id, artist_entity_id} — Auth: remove artist from lineup (event creator only)

Parameters

event_id e.g. 123

Response (with entity): { success: true, entity: {...}, music: [...], platforms: [...] } Response (no entity): { success: true, entity: null }

Returns the authenticated user's aggregated hub data: claimed entity, region, media, upcoming shows, connected platforms, stats. Response: { success: true, hub: {...} }

Returns public hub data for any user by username. No sensitive data (booking_email, manager, connected_platforms excluded). Includes profile-visible imported music. Response: { success: true, hub: {...} } 404 if user not found or no hub presence

Parameters

username

Public endpoint — no authentication required. Response: {success: true, results: [{type: 'artist', ...}, ...], total: N}

Parameters

q search query (required, min 2 characters)
region region slug (optional, scope to region)
type 'artist', 'venue', or 'event' (optional, filter to one type)
limit max results per type (default 20, max 50)

Public endpoint — no authentication required. Response: {success: true, venues: [...], total: N, limit: N, offset: N}

Parameters

region region slug (optional)
sub_region sub-region slug (optional)
city city name filter (optional)
limit results per page (default 24, max 100)
offset pagination offset (default 0)

Multipart form data: song_id (int) + artwork (file) Auth required. User must own the song or be admin. Validates image via finfo MIME detection (JPEG, PNG, WebP only). Saves to /uploads/artwork/ and updates music_songs.artwork_url.

Receives the authorization code from Audius OAuth, exchanges it for tokens using PKCE, fetches the user profile, and registers the connection. Expected query params: ?code=AUTH_CODE&state=STATE

Parameters

code e.g. AUTH_CODE
state e.g. STATE

OAuth-first: generates PKCE pair, stores verifier in session, returns the Audius authorization URL for the client to redirect to. Fallback: if body contains { "handle": "..." }, uses handle-based connection via the public Discovery API (no OAuth needed).

SoundCloud's API is restricted (no new app registrations), so we use a handle/URL-based connection. User provides their SoundCloud profile URL or handle, and we verify it exists via oEmbed. If the user already has a SoundCloud OAuth connection (from the login flow), we can auto-detect their handle from provider_data. Body: { "handle": "artist-name" } or { "url": "https://soundcloud.com/artist-name" }

Uses the existing Google OAuth credentials (GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET) but requests the youtube.readonly scope specifically for music import. Returns the Google OAuth authorization URL for the client to redirect to.

Returns full song data including artist, album, lyrics, and annotations.

Parameters

id e.g. 123

Removes the platform connection from music_imports. Previously imported songs remain in the catalog (not deleted). Body: { "platform": "audius" | "youtube" | "soundcloud" }

Rate limited: 100 events per hour per IP. Used by song-detail and album-detail pages for analytics + trending.

Body: { "song_id": 1 } Returns: { "success": true, "favorited": true/false }

Imports all tracks from the specified platform into the user's music catalog. Songs are imported with visible_on_profile = 0 (user must manually activate). Deduplicates by external_url — running import again skips already-imported tracks. Body: { "platform": "audius" | "youtube" | "soundcloud" }

Body: { "url": "...", "title": "...", "artist_name": "...", "album_title": "...", "platform": "bandcamp", "media_type": "audio", "features": "...", "track_number": 1, "duration": "3:45", "embed_code": "..." } Requires authentication. Auto-detects platform from URL if not specified. Creates artist if not found, finds or creates album, then calls addSong().

Returns all songs imported from external platforms for the authenticated user. Each song includes its visible_on_profile flag for toggle controls. platform — Filter by platform (audius, youtube, soundcloud) limit — Max results (default 100, max 500) offset — Pagination offset

Returns paginated list of songs with artist and album info.

Parameters

artist_id e.g. 1
album_id e.g. 1
platform e.g. bandcamp
media_type e.g. audio
q e.g. search
limit e.g. 20
offset

Returns songs the user has marked as visible on their hiphop.id profile. This is a public endpoint — no auth required. user_id — The user whose profile music to fetch (required) limit — Max results (default 50, max 100)

Parameters

user_id

PayPal redirects here after approval. Captures the order, marks purchase as completed, then 302-redirects to success or cancel page.

Returns: { purchased: bool } or { albums: [{id, purchased}] }

Parameters

album_id e.g. N (optional)

Input: { album_id: int } Returns: { success, approval_url, order_ref }

Creates a content_queue entry + audio_submission_sources entries. Requires login. Rate limited to 5 submissions per hour.

Parameters

song_id e.g. X)

Controls which imported songs appear on the user's public hiphop.id profile. Supports single toggle and bulk operations. Single toggle: Body: { "song_id": 123, "visible": true } Bulk toggle: Body: { "song_ids": [1, 2, 3], "visible": true } Show all / Hide all: Body: { "all": true, "visible": true } — makes all imported songs visible Body: { "all": true, "visible": false } — hides all from profile

Handles: PAYMENT.CAPTURE.COMPLETED, PAYMENT.CAPTURE.REFUNDED, PAYMENT.CAPTURE.REVERSED

Handles the redirect back from Google OAuth (YouTube scope). Exchanges the code for tokens, fetches channel info, and registers the YouTube connection for music import.

No description available.

No description available.

Body: { rsvp_id, action: "approve"|"decline" }

Requires: auth + event ownership

Parameters

id (event ID)

Body: { "date_id": 456 } Requires authentication. Must own the event that the date belongs to. Cancels an individual occurrence without affecting other dates. Returns success.

Body: { "event_id": 123, "reason": "optional reason" } Requires authentication. Must own the event. Sets event status to 'cancelled', cancels all scheduled dates, and notifies all RSVPed/waitlisted users. All sales are final — no refunds on cancellation.

Public endpoint. Returns all active categories with the count of active, public events that have at least one upcoming scheduled date.

Body: { qr_token: "xxx" }

POST /api/events/comments.php — Add comment Body: { event_id, body, parent_id? }

Parameters

event_id

Requires authentication. Creates event + slug + dates + tickets + tags. Body: { title, description, short_description, event_type, category_id, venue_name, venue_address, venue_city, venue_state, venue_country, digital_url, digital_platform, cover_image_url, is_free, max_capacity, visibility, timezone, requires_approval, allow_guests, max_guests_per_rsvp, dates: [{start_datetime, end_datetime, is_all_day}], tickets: [{name, price_cash, capacity, description}], tags: ["tag1", "tag2"], recurrence: {...}, status: "draft"|"active" }

Body: { "event_id": 123 } Requires authentication. Must own the event. Cascades to event_dates and event_recurrence via FK. Returns success.

Public endpoint — no authentication required. Returns event with dates, recurrence info, and creator info.

Parameters

event_id e.g. 123

city — filter by venue_city (partial match) state — filter by venue_state country — filter by venue_country date_from — events starting on/after (YYYY-MM-DD) date_to — events starting on/before (YYYY-MM-DD) is_free — 1 = free only, 0 = paid only page — page number (default 1) per_page — results per page (default 24, max 100)

Session required. GET: Returns user's favorited events POST: Toggle favorite on/off (requires CSRF) Body: event_id (int, required) Returns: {success: true, is_favorited: bool}

Parameters

limit (1-100, default 20), offset (default 0)

Public endpoint.

Parameters

limit (1-10, default 5)

Body: { host_id, action: "follow"|"unfollow" }

Parameters

username e.g. xxx
limit e.g. 20
offset
upcoming_only e.g. 1

Requires auth + event ownership. event_id — Event ID (required) Returns: rsvp_timeline — Daily RSVP registrations [{date, count}] revenue_by_tier — Revenue per ticket tier [{name, sold, revenue}] conversion_funnel — {views, registered, checked_in} capacity — {used, total, percentage}

Returns RFC 5545 VCALENDAR

Parameters

event_id

Body: { event_id, email?: string, user_id?: int }

Public endpoint. bbox — Bounding box: south,west,north,east (comma-separated floats) category_id — Filter by category ID (int) neighborhood — Filter by neighborhood slug date_from — Start date (YYYY-MM-DD) date_to — End date (YYYY-MM-DD) limit — Max features returned (1-500, default 200)

Requires authentication. Returns paginated list of user's events with dates attached.

Parameters

status e.g. active
limit e.g. 20
offset

Public endpoint.

Body: { event_id, action: "register"|"cancel", event_date_id?, guest_count?, guest_names?, rsvp_note? } Or for cancel: { rsvp_id, action: "cancel" }

Public endpoint. q — Full-text search term category — Category ID (int) city — City name (exact match) type — Event type: physical|digital|hybrid date_from — Start date (YYYY-MM-DD) date_to — End date (YYYY-MM-DD) is_free — 1 for free, 0 for paid sort — date|popular|recent|trending|nearby limit — Results per page (1-100, default 20) offset — Pagination offset (default 0) lat — Latitude (float, required when sort=nearby) lng — Longitude (float, required when sort=nearby) radius — Search radius in miles (1-200, default 50, used with sort=nearby)

Public endpoint. slug — Series slug (optional — returns single series with events) limit — Results per page for list mode (1-100, default 20)

Returns full event with dates, tickets, host info, rsvp_count

Parameters

slug

Body: { "event_id": 123, "cash_amount": 500 } Requires authentication. Must own the event. Creates a card template with source_type='event', card_type='tour'. Cash is embedded in the template to increase its visibility. Returns template data.

Accepts the simplified submit-content.php form (title, description, dates, venue, cover_image_url, ticket_url, source_url, category_id, neighborhood_id). Creates a 'draft' event for admin review. Works for both logged-in users and guests. Guest submissions store submitter_name/submitter_email in the event description.

city — filter by venue_city (partial match) state — filter by venue_state country — filter by venue_country date_from — events starting on/after (YYYY-MM-DD) date_to — events starting on/before (YYYY-MM-DD) is_free — 1 = free only, 0 = paid only page — page number (default 1) per_page — results per page (default 24, max 100)

POST /api/events/tickets.php — Purchase ticket Body: { event_id, ticket_id }

Parameters

event_id

Semi-public (works for logged-in and anonymous users). Body: { event_id: int }

city — filter by venue_city (partial match) state — filter by venue_state country — filter by venue_country date_from — events starting on/after (YYYY-MM-DD) date_to — events starting on/before (YYYY-MM-DD) is_free — 1 = free only, 0 = paid only source — eventbrite|ticketmaster|all (default: all) page — page number (default 1) per_page — results per page (default 24, max 100)

Public endpoint — no authentication required. Returns upcoming events sorted by next scheduled date. Only includes active events with at least one future scheduled date.

Parameters

limit e.g. 20
offset

Body: { "event_id": 123, "title": "...", "description": "...", ... } Requires authentication. Must own the event. Returns the updated event.

Accepts multipart/form-data with: - cover: image file (JPEG, PNG, WebP, GIF; max 5MB) - event_id: integer - csrf_token: string (or X-CSRF-Token header) Resizes to max 1200px wide, converts to WebP, saves to uploads/events/. Replaces any existing uploaded cover for this event. Returns: { success: true, url: "https://hiphop.world/uploads/events/..." }

Public endpoint. entity_id — Venue entity ID (required, int) limit — Results per page (1-100, default 20) offset — Pagination offset (default 0)

Public endpoint. q — Search query (required, min 2 chars)

Requires: admin auth Body: { user_id: int, verified: bool }

Body: { event_id, action: "promote"|"remove", waitlist_id }

POST /api/user/achievements.php — Trigger achievement check (action=check) GET Response: { "success": true, "achievements": {...grouped by category...}, "stats": {...} } POST Body: { "action": "check" } POST Response: { "success": true, "newly_earned": ["first_card", ...], "stats": {...} }

?limit=20 — events per page (max 100, default 20) ?offset=0 — pagination offset (default 0) Response: { "success": true, "events": [...], "total": 42, "stats": { "total_logins": 100, "last_login_at": "...", ... } } Requires authentication.

Parameters

limit e.g. 20

user_id (optional) — Get badges for a specific user (public profile view). If omitted, returns badges for the authenticated user. Response: { "badges": [ { "type": "...", "label": "...", "description": "...", "awarded_at": "...", "color": "...", "icon_svg": "..." }, ... ] }

Returns: { balance: 100000.00, formatted: "100,000.00" }

Returns the user's profile completeness score with item-level detail.

DELETE /api/user/connections.php — Unlink a connection by provider or connection_id PATCH /api/user/connections.php — Set primary wallet for a provider type

Accepts: { "password": "...", "confirmation": "DELETE", "reason": "optional" } - If user has a password_hash: requires correct password - If user has NO password (OAuth/wallet only): requires confirmation text "DELETE" - Anonymizes the user row (preserves ID for foreign key integrity) - Records deletion in account_deletions audit table - Cleans up related data (connections, verifications, sessions, tokens) - Destroys the PHP session

Returns a JSON file containing all data associated with the authenticated user: profile, connections, links, sessions, friendships, referral info.

Returns: { home_address, home_info, communities: [...], total_voting_power } - home_address: user's 6-digit place code (or null) - home_info: { community_name, land_id } if home_address is set - communities: array of communities owned by this user - total_voting_power: sum of voting_power across owned communities

No description available.

No description available.

Returns only the unread count — optimized for header badge polling. Minimal DB load: single COUNT(*) query. Response: {"success": true, "unread_count": 5} Requires authentication.

No description available.

GET: List notifications (paginated) Response: {"success": true, "notifications": [...], "total": 42, "unread_count": 5} POST: Mark notifications as read Body: {"action": "mark_read", "id": 123} or: {"action": "mark_all_read"} Response: {"success": true, "marked": 1} Requires authentication. POST requires CSRF.

Parameters

limit e.g. 20
offset
unread e.g. 0 (unread=1 for unread only)

No description available.

This endpoint is ONLY for changing an existing password. Setting a new password for the first time is handled by /api/auth/connect-email.php. Requires: current_password, new_password, confirm_password

GET — Returns current preferences (email_notifications global toggle) POST — Updates preferences { email_notifications: bool } The email_notifications toggle sets the email channel across ALL notification types. For granular per-type control, use /api/user/notification-prefs.php instead.

POST /api/user/profile-stage.php — Skip a stage { "stage": "music", "action": "skip" }

PUT /api/user/profile.php — Update user profile

No description available.

POST /api/user/recovery-wallet.php — Set recovery wallet (requires wallet signature) DELETE /api/user/recovery-wallet.php — Remove recovery wallet

Returns the user's referral code, referred count, and friend count.

GET: Returns list of active sessions for the authenticated user. DELETE: Revokes a specific session or all other sessions. Body: { "session_id": 123 } — revoke single session Body: { "all_others": true } — revoke all except current Requires authentication.

Accepts multipart/form-data with field: - banner (file) — image file (JPEG, PNG, WebP) Validates file type, size (<=2MB), dimensions (min 600x200, max 3000x1000). Resizes to max 1500px wide via GD. Deletes old local banner if replacing. Returns: { success: true, banner_url: "/assets/uploads/profiles/..." }

POST /api/user/verification.php — Submit verification request Body (POST): { "tier": "identity|artist|business", "real_name": "Full Legal Name", "proof_urls": ["https://...", "https://..."], "additional_info": "Optional explanation" }

Body: { "listing_id": 42 } Requires authentication + CSRF. Deducts Cash from buyer, credits seller, transfers card ownership.

Body: { "listing_id": 42 } Requires authentication + CSRF. Only the seller can cancel their own active listing.

Returns full card data including creator info, holder list, and stack info.

Parameters

id e.g. 123

Body: { "card_id": 123, "price": 500 } Requires authentication + CSRF. Returns the newly created listing data.

Returns paginated list of cards with creator username. Supports optional type filter and sort order (newest, holders, oldest).

Parameters

type e.g. audio
sort e.g. newest
limit e.g. 20
offset

Public endpoint — no authentication required. Returns paginated list of active card listings with card + seller details. Supports filtering by card type and multiple sort options.

Parameters

type e.g. audio
sort e.g. newest
limit e.g. 20
offset

Body: { "card_type": "audio", "title": "My Track", "description": "...", "media_url": "...", "external_link": "..." } Requires authentication. Costs CARD_MINT_COST cash. Returns the newly minted card data.

filter=created — cards the user minted (default) filter=held — cards the user holds (collected) Requires authentication.

Parameters

filter e.g. created|held
limit e.g. 20
offset

Returns paginated list of cards stacked at the specified place.

Parameters

place_id e.g. 123
limit e.g. 50
offset

Body: { "card_id": 123, "place_id": 456, "fee": 10 } Requires authentication. Stacks the specified card at the given place with an optional fee for other users to collect.

Returns aggregate statistics about the card ecosystem: total cards minted, total holders, active stacks, etc.

Body: { "card_id": 123, "to_user_id": 456 } Requires authentication. Transfers ownership of a card from the current user to the specified recipient.

Parameters

land_id

Parameters

id
days

GET ?action=my-boosts: User's boost history (auth required) Returns: {success, boosts} POST action=create: Create a new boost (auth required) Body: {land_id, boost_type, days} Returns: {success, boost} POST action=cancel: Cancel an active/scheduled boost (auth required) Body: {boost_id} Returns: {success}

Parameters

action e.g. my-boosts:

Body: {"land_id": int} Returns PayPal order ID for JS SDK approval flow.

Body: {"land_id": int, "paypal_order_id": string} Captures the payment, verifies amount, and assigns land to user.

No description available.

Parameters

code

No description available.

Parameters

land_id

Parameters

id

Parameters

land_id
limit
offset

Parameters

q e.g. search
land_id
page e.g. 1
per_page e.g. 20
sort e.g. voting_power

Body: {"land_id": int, "album_ids": [int]} Only the land owner can set featured audio.

Body: {"land_id": int, "profile_ids": [int]} Only the land owner can set featured profiles.

sort — 'views' (default), 'newest', or 'featured' limit — max results (default 25, max 50) Returns ranked territories with owner info and metric values. Cached for 5 minutes via Cache-Control header.

Returns minimal data for all 10,000 territories: id, claimed (0/1), mine (0/1), display_name, owner_username Optimized for the territory map canvas — only the fields needed for rendering. Response is cache-friendly (Cache-Control: 5 minutes).

Returns all lands owned by the authenticated user.

Parameters

code

days — lookback window in days (default 7, max 30) limit — max results (default 5, max 20) Returns the most-searched territory queries from the last N days.

Returns current pricing tier, price, sold count, and tier thresholds.

No description available.

limit — max results (default 10, max 20) Returns the most recently claimed territories with owner info.

No description available.

page — page number (default 1) per_page — results per page (default 50, max 100) filter — all|claimed|unclaimed (default all) search — search string

Returns full land details and tracks an impression event.

Parameters

id

Body: {"land_id": int, "event_type": "impression"|"click"} Hashes IP with sha256 for privacy. Auth optional for viewer tracking.

POST action=respond: Accept or decline a transfer Body: { action: "respond", token: string, response: "accept"|"decline" } Returns: { success, action, land_code } Requires auth. POST action=cancel: Cancel a pending transfer (sender only) Body: { action: "cancel", transfer_id: int } Returns: { success } Requires auth. GET action=pending: Get user's pending transfers Returns: { success, transfers: [] } Requires auth. GET action=check: Get transfer details by token (public) Returns: { success, transfer: {} } Public (token-based access).

Parameters

action e.g. pending
action e.g. check
token e.g. string

Body: {"land_id": int, "display_name": string, "description": string, "banner_url": string, "discord_link": string} Only the land owner can update.

Accepts multipart/form-data with fields: - land_id (int) — territory ID - banner (file) — image file (JPEG, PNG, WebP, GIF) Validates ownership, file type, size (<=5MB), dimensions (min 200x100, max 4000x2000). Resizes to max 1600px wide via GD. Deletes old local banner if replacing. Returns: { success: true, banner_url: "/assets/uploads/land/..." }

Community owners vote to promote or demote a card's visibility in the land's directory. Each community the user owns casts a vote weighted by that community's voting_power.

Community owners vote for who should control/represent a land. Each community the user owns in the land casts a vote weighted by that community's voting_power.

GET: Check if current user is watching. Returns: {watching, watcher_count} Requires authentication (session). Returns 401 if not logged in.

Parameters

land_id e.g. int

No description available.

Parameters

limit e.g. 10
offset

Public endpoint — no authentication required. Returns the list of valid categories with human-readable labels.

GET /api/directory/claim-request.php?verify=TOKEN — Email verification Rate limited: 3 requests per hour per IP. No auth required (public form).

Parameters

verify e.g. TOKEN

Body: { profile_id: int } Rate limit: 3/hour per user

Body: { "profile_id": 123 } Public endpoint — no authentication required. Increments the click counter and returns the profile's website URL.

Body: { name, email, subject, message } Public endpoint — NO authentication required. Rate limited: 3 submissions per 10 minutes per IP.

Body: { "entity_id": 123, "field_name": "phone", "suggested_value": "503-555-1234", "reason": "Number has changed", "submitter_email": "user@example.com" } CSRF: REQUIRED for logged-in users (X-CSRF-Token header) Rate limit: 10 requests per 300 seconds.

Body: { "title": "...", "url": "https://...", "description": "...", "logo_url": "https://...", "category": "music", "tags": ["hip-hop", "beats"] } Requires authentication. Creates a draft listing. Returns the listing.

Returns the user's directory profile, subscription, and analytics (impressions, clicks, CTR for last 30 days). Requires auth.

Public endpoint — no authentication required. Returns profile details with CRM fields stripped.

Parameters

id e.g. 123 or ?slug=some-slug

Public endpoint — no authentication required. Returns single entity or paginated entity list. Rate limit: 60 requests per 60 seconds.

Parameters

slug e.g. some-slug (single entity), ?id=123 (single), ?featured=1 (featured list), ?limit=24
offset

Parameters

entity_id e.g. X
period e.g. 30d

Body: { "entity_id": 123, "claimer_name": "...", "claimer_email": "...", "claimer_phone": "...", "claimer_role": "owner|manager|representative", "verification_method": "dns_txt|html_file|email|manual_review" } CSRF: REQUIRED (X-CSRF-Token header) Rate limit: 5 requests per 300 seconds. Returns claim data with verification instructions.

Body (multipart for upload): action=upload: entity_id, file (image upload) action=delete: entity_id, media_id action=reorder: entity_id, positions (JSON array of {media_id, position}) CSRF: REQUIRED (X-CSRF-Token header) Rate limit: 10 requests per 300 seconds.

Body: { "entity_id": 123, "name": "...", "tagline": "...", "short_description": "...", "long_description": "...", "website_url": "...", "phone": "...", "email": "...", "address_line_1": "...", "address_line_2": "...", "city": "...", "state_region": "...", "postal_code": "...", "country_code": "US", "hours_json": [...] } CSRF: REQUIRED (X-CSRF-Token header) Rate limit: 20 requests per 300 seconds.

No description available.

Public endpoint — no authentication required. Returns directory profiles with active featured_spot boosts from the ad engine. Used by the directory homepage "Featured" section.

Public endpoint — no authentication required. Returns active profiles with filters, sorted by newest/alphabetical/featured. Increments impression_count for returned profiles.

Parameters

category e.g. artist
city
country
monetization
featured e.g. 1
search
sort e.g. newest
limit e.g. 20
offset
recent e.g. 1

Public endpoint — no authentication required. Returns GeoJSON FeatureCollection for entities within bounding box. Rate limit: 20 requests per 60 seconds.

Parameters

sw_lat
sw_lng
ne_lat
ne_lng
category
type
verified

Requires authentication. Returns paginated list of user's listings (all statuses).

Parameters

limit e.g. 20
offset

Returns profiles where user_id matches current user OR submitter_email matches user's email.

Artists (or their representatives) can request removal of an auto-seeded profile. Rate limited: 3 requests per hour per IP. No auth required (public form).

Body: { "listing_id": 123 } Requires authentication. Must own the listing. Sets status to 'removed'. Cash is NOT refunded. Returns success.

No description available.

Public endpoint — no authentication required. Returns entities with faceted filters for category/city/type. Rate limit: 30 requests per 60 seconds.

Parameters

q
category
city
state
type
verified
lat
lng
radius_km
sort e.g. relevance
limit e.g. 24
offset

Public endpoint — no authentication required. Returns aggregate stats: total active, by category, recently added, countries.

Body: { submitter_name, submitter_email, name, website_url, primary_category, location_city, ... } Public endpoint — NO authentication required. Rate limited: 5 submissions per 5 minutes per IP. Honeypot: if `website` field (hidden) is filled, silently returns success (bot trap).

POST /api/directory/subscribe.php — Create directory subscription (auth required) POST body: { "profile_id": 123, "tier": "featured", "paypal_subscription_id": "I-xxx" }

Public endpoint — no authentication required. Returns lightweight suggestions for typeahead UI. Rate limit: 60 requests per 60 seconds.

Parameters

q e.g. port (min 2 chars)

Body: { "listing_id": 123, "cash_amount": 500 } Requires authentication. Must own the listing. Adds Cash to the listing to boost its ranking. Also extends the listing expiration by 30 days. Returns the updated listing.

Body: { "listing_id": 123, "title": "...", "url": "...", ... } Requires authentication. Must own the listing. Only draft or active listings can be updated. Returns the updated listing.

Body: { "website_id": 123, "method": "dns_txt"|"html_file"|"manual_review" } CSRF: REQUIRED (X-CSRF-Token header) Rate limit: Max 3 pending claims per user per hour Returns claim data with verification instructions.

Body: { "website_id": 123 } Validates the website exists and is published, increments click counter, and returns the official URL for redirect. Public endpoint — no authentication required.

User can only verify their own claims. Calls verifyClaim() to check DNS TXT record or HTML file, and returns the verification result.

Parameters

claim_id e.g. <int>
check e.g. 1

Returns website profile data, media entries, and checklist completion. Strips internal checklist_data state. Records an impression. Public endpoint — no authentication required.

Parameters

host e.g. <slug_or_host>

Cloned from /api/forum/channels.php with tier-gating via community-access.php.

Returns detailed information about a specific community identified by its 4-digit code. Returns 404 if the community does not exist.

Parameters

code

Creates a new community linked to a parent via neighbor code. Requires the user to be a member of the parent community and provide a valid neighbor code for the desired expansion slot.

No description available.

Cloned from /api/forum/history.php with tier-gating via community-access.php.

Parameters

channel_id
before
limit

Sets the user's primary community. If no community_id is provided, defaults to community 0000 (the default onboarding community).

No description available.

Returns a paginated list of active communities. Supports limit (max 100, default 20) and offset parameters.

Cloned from /api/forum/members.php with tier-gating via community-access.php. Params: channel_id (optional) -- If provided, only members who've posted in that channel

Parameters

channel_id

Returns the user's primary community assignment and available neighboring communities they can interact with or expand into.

No description available.

Cloned from /api/forum/messages.php with tier-gating via community-access.php. Params: channel_id (required) -- Discord channel ID after (optional) -- Message ID to fetch messages after (delta fetch)

Parameters

channel_id
after

Cloned from /api/forum/pinned.php with tier-gating via community-access.php.

Parameters

channel_id

Body (JSON): { "message_id": "...", "channel_id": "...", "emoji": "...", "action": "add"|"remove" } Cloned from /api/forum/reactions.php with tier-gating via community-access.php.

Returns: { activities: [...], stats: { messages_today: N, active_channels: N } }

Parameters

q
channel_id
limit

Body (JSON): { "channel_id": "...", "content": "..." } Body (multipart): channel_id, content (optional if attachment present), attachment (file) Cloned from /api/forum/send.php with tier-gating via community-access.php.

Body (JSON): { "channel_id": "..." } Cloned from /api/forum/typing.php with tier-gating via community-access.php.

Parameters

user_id e.g. X or ?community_id=X or ?global=1

DELETE /api/social/follow.php — Unfollow a user Body: { "user_id": 123 } POST: Rate limited (30 per 5 minutes). Creates a follow relationship. DELETE: Removes the follow relationship. Requires authentication + CSRF.

Returns paginated list of followers for the specified user. If the viewer is logged in, includes an isFollowing flag for each follower. Requires authentication.

Parameters

user_id e.g. 123
limit e.g. 20
offset

Returns paginated list of users the specified user follows. Requires authentication.

Parameters

user_id e.g. 123
limit e.g. 20
offset

POST /api/social/friends.php — Send a friend request PUT /api/social/friends.php — Accept or decline a friend request DELETE /api/social/friends.php — Remove a friend GET: Default: returns accepted friends for the current user. ?pending=1: returns pending friend requests for the current user. Optional: ?limit=20&offset=0 POST: Body: { "user_id": 123 } Rate limited: 20 per 5 minutes. PUT: Body: { "friendship_id": 456, "action": "accept" | "decline" } DELETE: Body: { "friendship_id": 456 } Requires authentication + CSRF.

Parameters

pending e.g. 1:

DELETE /api/social/team-members.php — Leave or kick a member POST actions: { "action": "join", "team_id": 123 } — Join a public team { "action": "invite", "team_id": 123, "user_id": 456 } — Invite a user to a team { "action": "respond", "invite_id": 789, "response": "accepted" | "declined" } — Respond to invite DELETE actions: { "action": "leave", "team_id": 123 } — Leave a team { "action": "kick", "team_id": 123, "user_id": 456 } — Kick a member (owner/admin only) Requires authentication + CSRF.

POST /api/social/teams.php — Create a new team PUT /api/social/teams.php — Update an existing team GET: ?id=123 — Get single team detail via getTeam() ?invites=1 — Get pending team invites for the current user Default — Get all teams for the current user Optional: ?limit=20&offset=0 POST: Body: { "name": "...", "team_type": "...", "description": "...", "avatar_url": "..." } Rate limited: 5 per hour. name and team_type are required; description and avatar_url are optional. PUT: Body: { "team_id": 123, "name": "...", "description": "...", "avatar_url": "..." } team_id is required; other fields are optional updates. Requires authentication + CSRF.

Parameters

id e.g. 123

Response: { success: true, balance: int, is_locked: bool, total_earned: int, total_spent: int }

Request body: { order_id: "PAYPAL_ORDER_ID", pack_key: "starter"|"growth"|... } Response: { success: true, credits: int, balance: int }

Request body: { pack_key: "starter"|"growth"|"pro"|"scale"|"mega" } Response: { success: true, order_id: "PAYPAL_ORDER_ID" }

limit (int, 1-100, default 20) offset (int, default 0) Response: { success: true, entries: [...], total: int }

Response: { success: true, packs: { starter: {...}, growth: {...}, ... } }

Request body: { amount: int, type: string, source_ref?: string, metadata?: object } Response (success): { success: true, amount: int, new_balance: int, ledger_id: int } Response (error): { success: false, error: "insufficient_credits"|"spend_locked"|"invalid_amount", ... } HTTP codes: 200 success, 400 invalid, 402 insufficient, 423 locked

Parameters

limit
offset

No description available.

No description available.

Body: {"to_username": "bob", "amount": 1000, "memo": "optional note"} Response: {"success": true, "from_balance": 9000, "to_balance": 1000, "transaction_id": 42}

Params: domain (optional) — Filter by target domain type (optional) — Filter by boost_type (default: network_banner) geo (optional) — Filter by target_geo (e.g., "portland", "us-west") category (optional) — Filter by target_category (e.g., "music", "events") Targeting columns (in ad_boosts table): target_geo — if set, only show when ?geo= or ?domain= matches. NULL = show everywhere. target_category — if set, only show when ?category= matches. NULL = show everywhere. target_hours — if set (format "HH-HH" like "18-24"), only show during those UTC hours. NULL = always. Returns: { success: true, ads: [{ id, boost_type, title, description, creative_url, target_url, alt_text, cta_text, cta_url, advertiser_name, impressions_today }] }

No description available.

boost_id=123 — analytics for a specific boost period=7|30 — time-series data for N days (requires boost_id) (omit) — aggregate analytics for all user's boosts

No description available.

POST /api/network/boost.php — Create a new boost (auth required) PUT /api/network/boost.php — Pause or resume a boost (auth required) POST body: { "type": "directory_boost|featured_spot|network_banner", "target_id": 123, "credits_per_day": 10, "daily_budget": 0, "ends_at": "2026-03-31 23:59:59", "starts_at": "2026-03-01", "target_geo": "portland-or", "target_category": "music-production", "target_hours": "18-24" } PUT body: { "boost_id": 1, "action": "pause|resume" }

POST /api/network/credits.php — Purchase credits (auth required) POST body: { "package": "starter|growth|scale", "paypal_order_id": "xxx" }

Public endpoint (no authentication required). Response is cached for 1 hour via Cache-Control header.

Accepts JSON body: { boost_id: int, type: "impression"|"click", domain: string, variant?: "a"|"b" } Returns: { success: true } Security: - Rate limited: 100 events per 60 seconds per IP. - Impression dedup: Same IP + boost + day = skip (stealth 200, not stored). - Clicks are NOT deduped (user may legitimately click multiple times). - Max request body size: 1 KB. - Domain validated as legitimate hostname. CORS: allows any origin for SDK compatibility (write-only endpoint).

Body: { "domain": "hiphop.audio", "status": "ok", "version": "1.0", "uptime": 12345 } Validates that the domain is in the SSO allowed domains list, then upserts into the network_heartbeats table.

matching HHW .env SYNC_API_KEY. Missing or mismatched key → 401. Expected payload (application/json): { "site_key": "obbium", // REQUIRED, must exist in engine_sites "source_user_id": 123, // OPTIONAL, site-local PK "email": "a@b.com", // REQUIRED "display_name": "Jane Doe", // OPTIONAL "role": "member", // OPTIONAL, default "member" "is_admin": 0, // OPTIONAL, default 0 "visibility": "public", // OPTIONAL, default "unlisted" "action": "create" // "create" | "update" | "delete" } Response: 200 { "success": true, "member_id": 42, "action": "upserted|deactivated" } 4xx { "success": false, "error": "..." } Stub status: file is live but no consumer site calls it yet — this is the Wave 3 delivery handle for the Membrane System. Local member-kit hooks will start POSTing here once the rollout switch is flipped.

Response is shaped EXACTLY like iaas_users rows: { members: [{name, rank, role, bio, tagline, disciplines, visibility}] } so community.php's array_merge() works unchanged. - Anonymous for ?visibility=public (read-only public view, CDN-cacheable) - Bearer SYNC_API_KEY for admin/full queries ?visibility=public Only public members (default) ?site_key=KEY Scope to members who have a facet on this site ?rank=officer Filter by ARC rank ?limit=50 Max results (1-200, default 50) ?search=term Name/email search Cache-Control: public, max-age=300 for anonymous public queries. Part of: SOUL_SYSTEM Body Layer → Membrane System

Parameters

visibility e.g. public

Request body: { "order_id": "PAYPAL_ORDER_ID", "package": "starter|growth|scale" } Response: { "success": true, "credits": 250, "balance": 500 }

Request body: { "package": "starter|growth|scale" } Response: { "success": true, "order_id": "PAYPAL_ORDER_ID" }

Accepts: { hw_user_id, email, display_name, avatar_url, phone, site_domain } Updates only non-null, non-empty fields (satellite sites cannot blank out HHW fields) Logs to network_activity with action = 'profile_synced'

Auth required: session-based (requireApiAuth). Returns Content-Type: text/html for display in new tab or iframe. Includes print-friendly CSS (@media print) for clean printing. Security: - User must be logged in (401 if not) - User must own the purchase (403 if not) - purchase_id must be a valid integer (400 if not) <!-- Dashboard link: <a href="/api/network/receipt.php?purchase_id=ID" target="_blank">Receipt</a> -->

Parameters

purchase_id

GET /api/network/status.php ?client_slug=X — filter to a specific client's sites Returns JSON: {overall, activeIncidents, sites[], resolvedIncidents[]}

Parameters

client_slug e.g. X

PayPal redirects the user here after they approve (or cancel) a subscription on PayPal's hosted approval page. This endpoint verifies the subscription state via the PayPal API and activates it locally, then redirects the user to the billing page with an appropriate success or error parameter. Method: GET (PayPal redirect — no CSRF, no JSON body) Params: subscription_id (GET query string, set by PayPal)

Cancels the subscription both on PayPal and in the local database. Access to premium features continues until the end of the current billing period (expires_at), after which the subscription transitions to 'expired'. Rate limit: 3 per hour (subscription_cancel) Body: {} (no body required) Response: {"success": true, "message": "Subscription cancelled. Access continues until ..."}

One-time setup endpoint that creates a PayPal Product and one Plan per tier/interval combination, then stores all plan IDs in the paypal_plans table. Idempotent when called with {"force": true} to recreate plans (e.g. after pricing changes). Method: POST only Body: {} or {"force": true} to override existing plan check Response: { "success": true, "product_id": "PROD-...", "plans": [ {"tier": "place", "interval": "month", "plan_id": "P-..."}, ... ] }

Returns the current subscription tier, full subscription record, recent payment history, and a feature-gate access map so the client can make gating decisions without additional round-trips. Method: GET only Response: { "success": true, "tier": "community", "tier_info": { ...SUBSCRIPTION_TIERS entry... }, "status": "active", "current_period_end": "2026-03-16T00:00:00", "subscription": { ...full record or null... }, "recent_payments": [ ...last 5... ], "feature_access": { "featureKey": true|false, ... } }

Initiates a PayPal subscription for the authenticated user, redirecting them to PayPal's hosted approval page. On success, returns an approve_url for the client to redirect to. Rate limit: 5 per hour (subscription_create) Body: {"tier": "place|community|land", "interval": "monthly|annual"} Response: {"success": true, "approve_url": "https://..."}

Parameters

date

No description available.

No description available.

Parameters

limit e.g. 20
offset

Requires authentication (logged-in user).

Parameters

id e.g. ...

No description available.

Also serves as /.well-known/jwks.json (via .htaccess rewrite)

Returns: { access_token, token_type, expires_in, refresh_token?, scope }

RFC: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo

Cost: 2 credits Body: {lyrics: string, mode: "draft"|"premium"|"triple_threat"} Returns: rhyme scheme analysis, syllable count, flow rating

GET ?action=list&type=image&favorited=1&search=...&limit=30&offset=0 POST action=favorite {asset_id} POST action=tag {asset_id, tags: [...]} POST action=delete {asset_id}

Parameters

action e.g. list
type e.g. image
favorited e.g. 1
search e.g. ...
limit e.g. 30
offset

Body (JSON): { "prompt": string, "bpm": int|null (60-200), "key": string|null ("C minor", "F# major", etc.), "subgenre": "boom_bap"|"trap"|"drill"|"lo_fi"|"jazz_rap"|"west_coast"|"soul"|"afrobeats", "density": float|null (0.0-1.0), "brightness": float|null (0.0-1.0), "duration": 15|30|60, "mode": "draft"|"premium" }

Body (JSON): { "prompt": string, "campaign_type": "single_release"|"album_rollout"|"tour"|"brand"|"event", "platforms": ["instagram","tiktok","twitter","email","youtube"], "timeline_days": int (7-90), "mode": "premium"|"triple_threat", "conversation_id": int|null } Campaigns always require premium mode minimum (75 base credits).

Body (JSON): { "conversation_id": int|null, "message": string, "mode": "draft"|"premium"|"triple_threat", "stream": bool } Supports SSE streaming, auto-classification, artist DNA personalization, conversation auto-creation, and Triple Threat multi-model consensus.

Parameters

id e.g. N

Parameters

history e.g. 1

Rate limited: 3 attempts/hour/IP Returns: {success, token, credits, expires_at}

GET — returns current artist DNA profile + generation stats POST {style_tags, vocabulary_level, influences, thematic_preferences, tone_profile, writing_samples}

Body (JSON): { "asset_id": int, // existing image asset to edit "edit_prompt": string, // what to change "aspect_ratio": "1:1"|..., "resolution": "1K"|"2K"|"4K" }

Body (JSON): { "prompt": string, "style": "album_cover"|"promotional"|"portrait"|"abstract"|"vintage", "aspect_ratio": "1:1"|"16:9"|"9:16"|"3:2"|"2:3"|"4:3"|"3:4"|"4:5"|"5:4", "resolution": "1K"|"2K"|"4K", "mode": "draft"|"premium" }

Body (JSON): { "prompt": string, "style": "freestyle"|"verse"|"hook"|"chorus"|"full_song", "mood": string|null, "bpm": int|null, "references": string|null, "mode": "draft"|"premium"|"triple_threat", "conversation_id": int|null }

GET ?action=list&status=draft GET ?action=get&id=123 POST action=create {title, product_type, card_type, ...} POST action=update {id, title?, description?, status?, ...} POST action=add_asset {product_id, asset_id, role} POST action=remove_asset {product_id, asset_id} POST action=submit {product_id} → bridges to card template POST action=quick_card {asset_id, title?, card_type?, price?, royalty_pct?} POST action=delete {id}

Parameters

action e.g. list
status e.g. draft

GET ?action=list&status=active GET ?action=get&id=123 GET ?action=assets&id=123 GET ?action=presets&tool=lyrics POST action=create {title, project_type, description} POST action=update {id, title?, description?, status?} POST action=delete {id} POST action=add_asset {project_id, asset_id, role} POST action=remove_asset {project_id, asset_id} POST action=reorder {project_id, asset_ids: [...]} POST action=save_preset {name, tool_type, config} POST action=delete_preset {preset_id} POST action=share {id} → generate share slug, mark public POST action=unshare {id} → mark private

Parameters

action e.g. list
status e.g. active

GET ?action=list&project_id=N → tracks with joined asset data GET ?action=get&id=N → single track with full details POST action=create { project_id, title } POST action=update { id, title?, beat_asset_id?, lyrics_asset_id?, voice_asset_id?, video_asset_id?, notes? } POST action=delete { id } POST action=reorder { project_id, track_ids: [3, 1, 2] }

Parameters

action e.g. list
project_id e.g. N

Body (JSON): { "text": string (max 500 chars), "voice": "kore"|"puck"|"charon"|"fenrir"|"aoede"|"leda"|"orus"|"zephyr" (Gemini voices) "mode": "draft"|"premium" } Primary: Gemini TTS → Fallback: ElevenLabs

GET /api/ai/video.php?action=poll&asset_id=N — Poll video generation status POST Body (JSON): { "prompt": string, "aspect_ratio": "16:9"|"9:16", "resolution": "720p"|"1080p", "duration": 4|6|8 }

Parameters

action e.g. poll
asset_id e.g. N

Params: ?sellToken=...&buyToken=...&sellAmount=...&chainId=1 Reads ZEROX_API_KEY from $_ENV. Forwards to https://api.0x.org/swap/v1/quote with API key header. Returns raw 0x response (contains pre-built transaction). Requires session auth.

Parameters

sellToken e.g. ...
buyToken e.g. ...
sellAmount e.g. ...
chainId e.g. 1

Requires session auth.

Returns historical HH Cash pricing based on completed orders.

Parameters

days e.g. 30 (1, 7, 30, 90, 365)

Accepts ?ids=bitcoin,ethereum,solana (CoinGecko IDs). Caches in exchange_price_cache table with 5-minute TTL. Public endpoint (no auth required), 60-second cache floor.

Parameters

ids e.g. bitcoin,ethereum,solana

POST: Insert new swap record Body: { chain, from_token, to_token, from_amount, to_amount, tx_hash, status, slippage_bps, swap_source } PATCH: Update swap status (e.g., initiated -> confirmed) Body: { id, status, tx_hash?, to_amount? } Requires session auth + CSRF.

GET: Returns user's watchlist from exchange_watchlists table POST: Adds token to watchlist (body: { chain, token_address, token_symbol, token_name }) DELETE: Removes token (body: { id } or { chain, token_address }) Requires session auth + CSRF for POST/DELETE.

Uses shared cash_wallets table from migration 021.

Body: {"amount_usd": 10.00, "crypto_currency": "eth"} Response: {success: true, order: {...}}

Response: {order: {order_ref, status, cash_amount, ...}}

Parameters

order_ref

Params: ?limit=20&offset=0 Response: {orders: [...], total: int}

Parameters

limit e.g. 20
offset

Response: {prices: {eth: 3000.00, ...}, user_rate: 0.10, discount: {community: 0, membership: 0, total: 0}}

Body: {"order_ref": "ORD-A1B2C3D4", "tx_hash": "0x..."} Response: {success: true, order: {...}}

Uses shared cash_wallets + cash_transactions tables from migration 021. page (default 1) limit (default 20, max 50) type (all | received | sent)

JSON body: sc2pulse_clan_id (int, required) — SC2Pulse clan ID clan_tag (string, required) — Clan tag (max 20 chars) clan_name (string, required) — Clan name (max 100 chars) reason (string, optional) — Why this clan should be tracked Requires login + CSRF. Inserts into clan_requests and approval_queue.

?action=list — List all cached clans ?action=get&id=<id> — Get a specific clan + members ?action=sync&id=<id> — Trigger sync for a clan (admin only) ?action=races&id=<id> — Get race distribution for a clan

Parameters

action e.g. list

?action=get&id=<external_id> — Get cached player data ?action=search&q=<query> — Search cached players by name/tag ?action=claim&player_id=<local_id> — Claim a gaming profile (requires login + Battle.net match) ?action=mmr-history&id=<external_id> — Get MMR history (syncs if empty) ?action=live&id=<external_id> — Fetch live data from SC2Pulse ?action=matches&id=<external_id> — Get match history from SC2Pulse

Parameters

action e.g. get
id e.g. <external_id>

?q=<query> — Search SC2Pulse for clans by name/tag (min 2 chars) Requires login. Returns enriched results with local tracking/request status.

Parameters

q e.g. <query>

No description available.

GET?action=bans — list guild bans GET?action=audit_log — view audit log POST action=unban — unban a user POST action=timeout — timeout a user POST action=remove_timeout — remove timeout

Parameters

action e.g. bans

No description available.

No description available.

No description available.

No description available.

Parameters

channel_id
before
limit

Uses the user's stored Discord OAuth2 access token.

Params: channel_id (optional) — If provided, only members who've posted in that channel

Parameters

channel_id

No description available.

Params: channel_id (required) — Discord channel ID after (optional) — Message ID to fetch messages after (delta fetch)

Parameters

channel_id
after

Parameters

channel_id

Body (JSON): { "message_id": "...", "channel_id": "...", "emoji": "...", "action": "add"|"remove" }

Params: q (required) — Search query (min 2 chars) channel_id (optional) — Filter by channel limit (optional) — Max results (default 20, max 50)

Parameters

q
channel_id
limit

Body (JSON): { "channel_id": "...", "content": "..." } Body (multipart): channel_id, content (optional if attachment present), attachment (file)

No description available.

Parameters

chat_id e.g. <telegram_chat_id>
limit e.g. 50
before e.g. <id>
after e.g. <id>

No description available.

Body: { "channel_id": "...", "visible": true/false }

Body (JSON): { "channel_id": "..." }

Body: { "lyrics_id": 1, "start_offset": 0, "end_offset": 50, "selected_text": "highlighted text", "annotation_text": "This means..." } Requires authentication. Creates a pending annotation for review.

Returns all approved annotations for the specified lyrics.

Parameters

lyrics_id e.g. 1

Body: { "song_id": 1, "content": "[VERSE 1]\nLyrics here..." } Requires authentication. Creates or updates lyrics entry for the song.

Returns previous versions of the lyrics content.

Parameters

lyrics_id

Body: { "annotation_id": 1, "direction": "up" } Requires authentication. Prevents self-voting (handled by voteAnnotation).

Requires authentication (session or Bearer token). domain = optional domain key filter (e.g. 'audio', 'cards') limit = max results (default 50, max 100) offset = pagination offset (default 0) Returns: {success: true, files: [...], quota: {...}, pagination: {limit, offset, total}}

Public endpoint — no session required, but requires a valid HMAC-signed URL. id = file ID exp = expiration timestamp sig = HMAC signature Serves the file with correct Content-Type, Content-Disposition, and cache headers.

Requires authentication (session or Bearer token). Accepts multipart/form-data with: - file: the uploaded file (required) - domain_key: category key e.g. 'audio', 'cards', 'documents' (optional, defaults to 'general') - metadata: JSON string of extra metadata (optional) Returns: {success: true, file: {id, original_name, mime_type, file_size, signed_url, created_at}}

Body (single): { "id": 123 } Body (mark all): { "all": true } Requires authentication.

If the user is not logged in, returns {authenticated: false, count: 0}.

Body: { site_id, name, email, event_type, event_date, message, phone?, venue?, budget_range?, expected_attendance?, organization?, location? }

Parameters

status e.g. new|read|replied|booked|declined

Parameters

slug e.g. )

No description available.

No description available.

No description available.

Parameters

module_type e.g. music|videos|gallery

No description available.

No description available.

No description available.

Parameters

username

No description available.

No description available.

No description available.

Handles: PAYMENT.CAPTURE.COMPLETED — recovery grant if capture-order failed PAYMENT.CAPTURE.REFUNDED — partial/full refund reversal (delta-based) PAYMENT.CAPTURE.REVERSED — chargeback reversal (delta-based) Method: POST only CORS: None (server-to-server) Uses a separate webhook ID (PAYPAL_CREDITS_WEBHOOK_ID) from the main subscription webhook to keep concerns separated.

Receives and responds to Discord Application Command interactions (slash commands) and message component interactions (buttons, selects). Discord requires: 1. Ed25519 signature verification on every request (401 if invalid) 2. Respond to PING (type 1) with {"type": 1} 3. Respond within 3 seconds (or defer with type 5) Method: POST only CORS: None (server-to-server)

Receives server-to-server event notifications from PayPal. Verifies the webhook signature using PayPal's verification API, then dispatches each event to the appropriate handler. Always returns HTTP 200 so PayPal does not retry on application-level errors (per PayPal webhook best practices). Method: POST only CORS: None (server-to-server) CSRF: None (not browser-initiated) Supported event types: BILLING.SUBSCRIPTION.ACTIVATED PAYMENT.SALE.COMPLETED BILLING.SUBSCRIPTION.CANCELLED BILLING.SUBSCRIPTION.EXPIRED BILLING.SUBSCRIPTION.SUSPENDED BILLING.SUBSCRIPTION.PAYMENT.FAILED

Receives updates from the Telegram Bot API. Verifies the secret token header, dispatches commands, and always returns HTTP 200. Method: POST only CORS: None (server-to-server) CSRF: None (not browser-initiated)

POST data: site_key: string (required) — site to scan (e.g. 'hiphop.world') Returns JSON: success: bool score: int (0-100) details: array with pillar scores checks_run: int duration_ms: int timestamp: string (ISO 8601)

No description available.

GET ?from=YYYY-MM-DD&to=YYYY-MM-DD&site_key=X&metrics=messages,sla,intent,queue,peak_hours GET ?export=csv

Parameters

from e.g. YYYY-MM-DD
to e.g. YYYY-MM-DD
site_key e.g. X
metrics e.g. messages,sla,intent,queue,peak_hours

Body: { "template_id": 123 } Admin auth required. Approves a pending template, sets it to 'active', and mints the creator's first card. Returns template and card data.

Body: { "verification_id": 123 }

No description available.

GET requests: (no params) — Returns booking_config (7 day rows) + booking_blackout dates POST requests (JSON body): {"action": "update_schedule", "day_of_week": 1, "start_time": "09:00", "end_time": "17:00", "is_available": 1} {"action": "add_blackout", "date": "2026-03-15", "reason": "Holiday"} {"action": "remove_blackout", "date": "2026-03-15"}

Fetches the live HTML of a registered site, runs the AlignmentScanner, and returns formatted pass/fail/warning results. Body: { "site_key": "..." } Returns: { success: true, data: { results: [...], summary: { pass: N, fail: N, warn: N } } }

Exports brand configuration in three formats: - CSS: Custom properties in :root {} block - Tailwind: tailwind.config.js theme extend - Figma: Figma Tokens JSON (color, typography, spacing groups)

Parameters

site_key e.g. X
format e.g. css|tailwind|figma

Parameters

site_key e.g. X

No description available.

Loads brand config, phase data, and assets, then generates a brand-guidelines PDF via the Pdf component and streams it as a downloadable attachment.

Parameters

site_key

GET ?site_key=X — Loads saved brand config from DB and renders preview POST (form data) — Uses posted color/font values for live preview (unsaved data) Renders a complete HTML page with: - Brand fonts (Google Fonts link) - Brand CSS variables - brand_header(), brand_hero(), sample cards, brand_footer()

Parameters

site_key e.g. X

GET - List all quotes (optionally filtered by ?site_key=X) POST - Generate a new quote for a site (auto-calculates from brand phases) PATCH - Update quote status (takes quote_id, status, optional notes)

Parameters

site_key e.g. X)

Fetches the live HTML of a registered site and extracts Phase 2 visual identity: - Colors: hex/rgb from CSS/inline styles, mapped to primary/secondary/accent/bg/surface/text - Fonts: font-family declarations, Google Fonts links, heading vs body - Logo: <img> with "logo" in src/alt/class/id - Favicon: <link rel="icon"> or <link rel="shortcut icon"> Saves to: engine_sites.branding, engine_brand_phases (Phase 2), engine_brand_assets Body: { "site_key": "..." } or form POST with site_key

Multipart/form-data with: - site_key (required) - asset_type (required: logo, logo_mark, favicon, icon_set, og_image, hero_image, pattern) - file (required: uploaded image file) - label (optional) - csrf_token (required) Admin auth required.

GET requests: ?summary=1 — Stats summary + per-site counts ?limit=50&offset=0 — List consultations ?site_key=X — Filter by site ?status=pending — Filter by status ?upcoming=1 — Upcoming only (preferred_date >= today) ?upcoming=0 — Past only (preferred_date < today) PATCH requests (JSON body): {"id": 123, "status": "confirmed"} — Update status {"id": 123, "admin_notes": "Called client"} — Update admin notes {"id": 123, "status": "confirmed", "admin_notes": "..."} — Update both

Parameters

summary e.g. 1

Parameters

user_id e.g. 123

POST /api/admin/crypto-orders.php — Admin actions on orders (admin only) GET params: action=stats Return aggregate stats action=list (default) Return filtered/paginated order list status, crypto_currency, search, date_from, date_to, limit, offset, sort, order POST body: {"action": "complete|fail|expire|retry", "order_id": 123, "reason": "optional note"}

No description available.

GET actions: ?action=tree — Full hierarchical category tree ?action=get&id=123 — Single category detail POST actions (require CSRF): action=create — Create category (name, slug, parent_id, color, icon, description) action=update — Update category (category_id, ...fields) action=delete — Delete category (category_id) — fails if entities assigned action=reorder — Reorder categories (positions: [{category_id, position}])

Parameters

action e.g. tree

GET: List pending claims via getPendingClaims() ?limit=50 POST actions (require CSRF): action=approve — Approve a pending claim (claim_id required) action=reject — Reject a pending claim (claim_id required, optional reason)

Parameters

limit e.g. 50

GET actions: ?action=list — Paginated entity list (filterable: status, category, city, type, search, verified) ?action=get&id=123 — Single entity detail ?action=queue — Pending moderation queue ?action=corrections — Pending corrections queue POST actions (require CSRF): action=approve — Approve pending entity (entity_id) action=reject — Reject pending entity (entity_id, reason) action=flag — Flag entity for review (entity_id, reason) action=merge — Merge duplicate into canonical (canonical_id, duplicate_id) action=update — Admin update entity fields (entity_id, ...fields) action=delete — Soft-delete entity (entity_id)

Parameters

action e.g. list

GET actions: ?action=list&entity_id=123 — List publications for an entity ?action=sites — List all publication target sites POST actions (require CSRF): action=publish — Publish entity to a site (entity_id, site_key) action=unpublish — Remove entity from a site (entity_id, site_key) action=feature — Toggle featured status on a site (entity_id, site_key) action=auto-publish — Auto-publish entity to all eligible sites (entity_id)

Parameters

action e.g. list
entity_id e.g. 123

GET: List websites with optional filters: ?status=<status>&category=<cat>&search=<term>&is_featured=0|1&limit=25&offset=0 POST actions (require CSRF): action=create — Seed a new website entry action=update — Update website fields action=toggle_featured — Toggle is_featured flag action=toggle_verified — Toggle is_verified flag

Parameters

status e.g. <status>
category e.g. <cat>
search e.g. <term>
is_featured e.g. 0|1
limit e.g. 25
offset

GET actions: ?action=pending — List pending profiles ?action=list — List all profiles with filters (status, category, score_min, qualifier, monetization, search, email, sort, limit, offset) ?action=stats — Aggregate stats + pending count ?action=export-csv — CSV export with filters POST actions (require CSRF): action=approve — Approve a pending profile action=reject — Reject a pending profile (with reason) action=update-crm — Update CRM/intelligence fields action=toggle-featured — Toggle featured flag action=toggle-verified — Toggle verified flag

Parameters

action e.g. pending

Body: { "action": "create"|"edit"|"delete", ... }

No description available.

No description available.

Body: { "action": "sync" }

POST /api/admin/discord/channels.php — Create a channel PUT /api/admin/discord/channels.php — Modify a channel DELETE /api/admin/discord/channels.php — Delete a channel

PUT /api/admin/discord/members.php — Modify member roles DELETE /api/admin/discord/members.php — Kick or ban a member

DELETE /api/admin/discord/messages.php — Delete a message POST /api/admin/discord/messages.php — Pin or unpin a message

No description available.

No description available.

No description available.

GET /api/admin/domain-health.php — returns JSON payload matching $domainMapData structure

GET (admin): {site_key} — Return current onboarding status + data

Returns: {success, logs: [...], total, page, per_page}

POST /api/admin/enterprise.php — Admin actions on partners (admin only) GET params: action=list (default) Return filtered/paginated partner list action=stats Return aggregate stats action=detail&id=X Return full partner details including API keys and OAuth clients status, tier, search, limit, offset POST body: {"action": "approve|suspend|activate|update_tier|top_up", "partner_id": 123, ...}

Parameters

action e.g. list

GET list — optional filters: ?slug=, ?skill_affected=, ?status=, ?severity= GET detail — ?slug=<slug> returns a single row POST create — admin auth + CSRF; creates a new failure mode row PATCH update — admin auth + CSRF; increments occurrence_count, bumps last_seen_at, and optionally updates status / notes All responses include X-API-Version: v1 via jsonResponse().

Parameters

slug e.g. ,

No description available.

GET: (no params) — list all featured artists (active + inactive) ?action=search&q= — search entities to add POST: ?action=add — add entity to featured list ?action=remove — remove entity from featured list ?action=reorder — reorder featured list ?action=toggle — toggle is_active

Parameters

action e.g. search
q

Endpoints: POST ?action=save — Save or update a filter preset GET ?action=list — List all presets for current admin DELETE ?preset_id=X — Delete a specific preset Requires admin authentication and CSRF token for POST/DELETE.

Parameters

action e.g. save

Distributes Hip Hop Cash from the treasury wallet to a user. Used after a user purchases Cash with USD/crypto. Body: {"to_username": "bob", "amount": 10000, "memo": "Cash purchase - Invoice #123"} Response: {"success": true, "to_balance": 10000, "treasury_balance": 999990000, "transaction_id": 42}

GET: ?action=pending — entities pending moderation ?action=stats — overall hub statistics POST: ?action=approve_entity — approve entity + assign region ?action=reject_entity — reject entity ?action=assign_region — assign entity to region ?action=add_lineup — add artist to show lineup DELETE: ?action=remove_lineup — remove artist from lineup

Parameters

action e.g. pending

Endpoints: - POST ?action=assign — assign message(s) to user or queue - GET ?action=list_queues — list available queues + admin users - GET ?action=list_assignments — list current assignments with metadata - POST ?action=auto_assign — batch auto-assign based on intent type - POST ?action=update — update assignment notes - DELETE ?action=unassign — remove assignment

Parameters

action e.g. assign

POST requests (JSON body): {"action":"add_tag","submission_id":1,"tag_name":"urgent","color_hex":"#FF5733"} {"action":"remove_tag","submission_id":1,"tag_name":"urgent"} {"action":"bulk_tag","submission_ids":[1,2,3],"tag_name":"urgent","color_hex":"#FF5733"} GET requests: ?action=list — list all tags with counts ?action=autocomplete&q=pre — autocomplete tag names DELETE requests: ?tag_name=X — delete tag definition from tag_metadata

Parameters

action e.g. list

Requires admin session authentication.

Parameters

action e.g. queue

Returns JSON with signups, first_purchase_conversion, land_claims, credit_purchases, founder_slots, directory_stats, recent_signups, error_rate, generated_at. Also used as a library: when LAUNCH_METRICS_LIB_ONLY is defined, only the buildLaunchMetrics() function is loaded (no HTTP handler).

Parameters

action e.g. stats

Table name `1vs_categories` is backticked at every reference because it starts with a digit.

Parameters

tree e.g. 1

?format=matrix (default) — { sites, kits, matrix: { site_key: {kit: 1|0} } } ?format=catalog — { sites: [ {site_key, domain, site_type, status, kits{}, feature_tags[]} ] } ?site=<site_key> — filters either response to a single site Always returns X-API-Version: v1 via jsonResponse(). Pure GET; no mutations.

Parameters

format e.g. matrix

GET requests: ?view=all&limit=50&offset=0&status=new ?view=site&site_key=X&limit=50&offset=0 ?summary=1 POST requests (JSON body): {"action":"mark_status","ids":[1,2,3],"status":"read"} {"action":"toggle_exclude","site_key":"oregontires"}

Parameters

view e.g. all
limit e.g. 50
offset
status e.g. new

Parameters

id e.g. ...)

Read-only — no CSRF.

Parameters

task_id e.g. N

Parameters

site_key e.g. X

Parameters

site_key e.g. X

Auth (one of): 1. Valid hiphop.world admin session (human) 2. Authorization: Bearer <PM_BOARD_API_KEY> header (AI via Stop hook) Arbitration (AI writes): - manual_lock=1 → 403 "locked" - human_touched_at <24h + differing status change → 409 "deferred_to_human" Status-change side effects: - Always inserts into 1vs_network_pm_events - Human status change → sets human_touched_at=NOW(), last_actor='human' - Human status='done' → also sets manual_lock=1 - AI write → sets last_actor='ai'

Parameters

site_key e.g. X,

Parameters

site_key e.g. X
limit e.g. 20

GET - List domains with optional filters GET ?action=stats - Aggregate statistics PUT ?id=X - Update domain fields

Parameters

action e.g. stats

POST ?action=add_repo - Add a GitHub repo to track POST ?action=sync&repo_id=X - Sync recent commits for a repo POST ?action=sync_all - Sync all tracked repos GET ?action=repos - List all tracked repos GET ?action=commits&repo_id=X - Get commits for a repo GET ?action=feed - Latest 50 commits across all repos DELETE ?action=remove_repo&repo_id=X - Remove a tracked repo

Parameters

action e.g. add_repo

POST ?action=check&domain_id=X - Run health check on single domain POST ?action=check_all - Run health checks on all Live domains GET ?action=results&domain_id=X - Health check history for a domain GET ?action=latest - Latest health check per Live domain

Parameters

action e.g. check
domain_id e.g. X

GET - List tasks with filters GET ?action=board - Tasks grouped by status (Kanban) GET ?action=activity - Recent activity log POST - Create a new task POST ?action=log_activity - Manually log activity PUT ?id=X - Update a task DELETE ?id=X - Delete a task

Parameters

action e.g. board

Parameters

action e.g. list

No description available.

Body: { "template_id": 123, "reason": "Does not meet quality guidelines" } Admin auth required. Rejects a pending template and refunds any embedded Cash to the creator. Returns template data with refund info.

Body: { "verification_id": 123, "reason": "Insufficient documentation" }

POST /api/admin/revenue/actions Body: {id, action: 'view'|'dismiss'|'execute'}

No description available.

No description available.

No description available.

No description available.

No description available.

No description available.

1. cross_site_journey — % of network members with activity on 2+ sites in last 30 days 2. credit_velocity — avg credits spent per active wallet per day, last 30 days 3. creator_retention — % of members active in the prior 30-day window still active today 4. time_to_first_value — approx median seconds between user creation and first credit ledger entry 5. north_star — composite score: cross_site_journey*0.4 + creator_retention*0.4 + normalized credit_velocity*0.2 Every query is wrapped in try/catch. If a table is missing (dev env) the metric returns null and a warning is appended to the `warnings[]` array — the endpoint never crashes. Spec: docs/SOUL_SYSTEM.md lines 1570-1589

GET ?action=list — paginated audit log (filters: event_type, domain, user_id, date_from, date_to) GET ?action=stats — 24h summary: failure rate, top errors by domain, suspicious IPs, token table size

Parameters

action e.g. list

Admin auth required. Returns templates filtered by status for admin review.

Parameters

status e.g. pending|approved|rejected
limit e.g. 20
offset

Parameters

id

GET /api/admin/user-roles.php?action=available — list all assignable role slugs POST /api/admin/user-roles.php — assign role: { user_id, role_slug } DELETE /api/admin/user-roles.php — remove role: { user_id, role_slug }

Parameters

user_id

PUT /api/admin/users.php — Toggle admin role (admin only)

Parameters

status e.g. pending|approved|rejected|all (default: pending)

GET ?action=list - List all webhooks for site GET ?action=logs&webhook_id=X - View delivery logs for webhook POST ?action=create - Create new webhook POST ?action=update - Update webhook (requires webhook_id) POST ?action=delete - Delete webhook (requires webhook_id) POST ?action=test&webhook_id=X - Send test payload

Parameters

action e.g. list

No description available.

No description available.

No description available.

No description available.

No description available.

No description available.

No description available.

No description available.

No description available.

HHW-native implementation. The kit passthrough was broken: - Kit redirected to /member/profile which doesn't exist on HHW - Kit's startAuthenticatedSession() doesn't set $_SESSION['username'] which HHW's profile.php and other pages expect Query parameters: token=<token> — Magic link token from email email=<email> — Email address for verification return=<path> — Optional relative redirect path after success

HHW-native implementation (replaces passthrough to kit's broken sendMagicLink()). Kit's MemberMail::sendMagicLink() does not exist — this file sends email directly via PHPMailer using the same pattern as includes/mail.php. Request body: { "email": "user@example.com", "csrf_token": "..." } Response: { "success": true, "message": "Check your email for a sign-in link" } Tables used (no prefix on HHW): - magic_link_tokens (email, token_hash, expires_at, created_at) - rate_limit_actions (action, identifier, created_at)

No description available.

No description available.

No description available.

Body: { provider: 'linkedin'|'discord'|'google'|... }

No description available.

No description available.

No description available.

No description available.

No description available.

No description available.

This endpoint queries blog_posts with optional site_key filter and degrades gracefully to an empty list when the table is absent — so Tier B sites can call it today and will get real data once the table ships.

Parameters

site_key e.g. bars.gives
limit e.g. 10
offset
slug e.g. ...

No description available.

No description available.

No description available.

Parameters

action e.g. list

Parameters

action e.g. list

Parameters

featured e.g. 1

No description available.

Parameters

action e.g. list

Returns: { events: [...], region: ?string, limit: int, offset: int }

Parameters

region e.g. dmv

Parameters

region e.g. dmv

No description available.

Rate limit: 60 requests/min per IP

Parameters

action e.g. list

No description available.

Parameters

action e.g. artists

Accessible as: - /api/v1/openapi.php - /api/v1/openapi.json (via .htaccess rewrite)

No description available.

Parameters

action e.g. me

No description available.

No description available.

One-time body: { "order_id": "PAYPAL_ORDER_ID", "tier": "quick_start", "payment_type": "one_time" } Recurring body: { "subscription_id": "PAYPAL_SUB_ID", "tier": "full_service", "payment_type": "recurring" } Response: { "success": true, "client_id": 1, "tier": "...", "redirect": "/marketing" }

Request body: { "tier": "quick_start|...|full_service", "payment_type": "one_time|recurring" } Response (one-time): { "success": true, "order_id": "PAYPAL_ORDER_ID" } Response (recurring): { "success": true, "subscription_id": "...", "approval_url": "..." }

GET Response: { "files": [...] } POST Response: { "success": true, "file_id": 1, "filename": "..." }

POST — Update onboarding checklist state (action=update_onboarding). Persists item completion to marketing_clients.onboarding_data JSON column. Response (GET): { "is_client": true, "tier": "growth_accelerator", "tier_name": "Growth Accelerator", ... } Response (POST): { "success": true, "onboarding_data": {...}, "progress": "3/5" }

GET Response: { "updates": [...] } POST Request: { "client_id": 1, "title": "...", "body": "...", "attachment_url": "..." } POST Response: { "success": true, "update_id": 1 }

Request body: { name, email, phone?, links?, budget_range, primary_goal, goal_details?, timeline, location?, how_found?, consent } Response: { success: true, lead_id: 123 }

GET Response: { "referral_code": "AB12CD34", "referral_url": "https://..." } POST Request: { "code": "AB12CD34" } POST Response: { "valid": true, "referrer_name": "...", "discount_percent": 10 }

Request body (JSON): { "event": "page_view", "data": { ... }, "session_id": "optional" } Response: { "ok": true }

No session/CSRF — PayPal sends server-to-server POST requests. Always returns 200 for valid (even unhandled) events to prevent retries. Returns 400 only for invalid signatures.

Parameters

slug e.g. username

No description available.

No description available.

Body JSON: { "user_id": int, "action": "follow"|"unfollow" }

Parameters

user_id e.g. X
limit e.g. 20
offset

Parameters

limit e.g. 20
offset

Body: { amount: number, currency?: string (default USD), type: "one_time" | "recurring", paypal_transaction_id?: string, paypal_subscription_id?: string, message?: string, anonymous?: bool, donor_name?: string, donor_email?: string } If the user is logged in, links donation to their user_id. Rate limited: 10 per hour per IP.

Returns total donated, donor count, and last 10 non-anonymous completed donations. No authentication required. Rate limited: 30 per minute. Cache-friendly with 60-second Cache-Control.

POST actions: action=create_key — generate new API key (name, environment, scopes) action=revoke_key — revoke an API key (key_id) action=create_client — register OAuth client (client_name, redirect_uris, scopes, etc.) action=update_profile — update partner profile (company_name, website, description, contact_email)

Body: {"amount": 500} The amount is deducted from users.cash_balance and added to enterprise_partners.cash_balance.

Requires admin authentication. Params: site_key (required), limit (default 50), offset (default 0)

Requires admin authentication. Body: { submission_id: int } or { site_key: string, mark_all: true }

POST /api/engine/contact-form/submit Headers: X-Site-Key Body (JSON): {site_key, name, email, subject, message, _hp_email}

Boots the engine, handles CORS with domain whitelist, and delegates to the component API router.

Parameters

site_key e.g. <key>

Usage: GET /api/engine/network-sites CORS: Restricted to engine_sites domains

Usage: GET /api/engine/site-bundle?site_key=<key> Cache: public, max-age=300 (5 minutes)

Parameters

site_key e.g. <key>

Requires authenticated session. Rate limited to 3 req/min per user. Returns the claimed slot info or error reason.

POST /api/founder/status.php — Claim founder slot (auth required) GET responses are cached for 10 seconds and rate limited to 30 req/min per IP. If the caller is authenticated, includes my_slot info.

Parameters

input
country

Parameters

place_id

Public endpoint — no authentication required.

Parameters

slug (required) region slug

Public endpoint — no authentication required.

Parameters

status 'active' (default), 'draft', 'archived' (admin only)

No description available.

Admin auth required.

Parameters

site_key e.g. xxx

Public endpoint — no auth required

Public endpoint — no auth required

Public endpoint — no auth required

Public endpoint — no auth required

Public endpoint — no auth required

Body: { queue_id } Requires admin authentication. Uses embedded Cash (already deducted at submit).

Body: { queue_id, action: 'approve'|'reject' } Requires admin authentication. Refunds Cash on rejection.

Body: { content_type, content_id, cash_embedded? } Requires authentication. Optionally locks Hip Hop Cash into the submission.

Public endpoint — no authentication required. Returns template with mint count and creator info.

Parameters

template_id e.g. 123

Public endpoint — no authentication required. Returns paginated list of active templates sorted by the given criteria.

Parameters

sort_by e.g. cash_embedded|mint_count|created_at
source_type e.g. event
limit e.g. 20
offset

Body: { "template_id": 123 } Requires authentication. Costs 100 Cash. Creator gets royalty, remainder locked in card. Returns new card data and mint receipt.

Requires authentication. Returns paginated list of templates created by the user (all statuses).

Parameters

limit e.g. 20
offset

POST JSON: {quote_id: int, action: 'accept'|'decline', notes?: string} Returns: {success: true, data: quote}

POST multipart/form-data: site_key, asset_type, file Returns: {success: true, data: {url, asset_type, site_key}}

Body: { "artist_id": 1 } Requires authentication. Links the artist to the current user's account.

Returns artist profile data and their music catalog.

Parameters

id e.g. 1 or ?slug=mental-stamina or ?user_id=1

Writes a new entry to hd_entities (entity_type='artist') + hd_vertical_music with moderation_status='pending'. Admin approves via /admin/?page=queue. Body: { name, hometown, city, state_region, bio, genres, instagram_handle, youtube_handle, spotify_id, website_url, social_links } Only name + hometown (or city/state) are required.

Parameters

page e.g. queue.

Returns: {newly_awarded: [...]}

No description available.

Requires: logged in + SoundCloud connected with stored access token

Body: { "email": "...", "reset_url_template": "https://bgpdx.com/reset-password?token={TOKEN}", "site_name": "Bamboo Grove PDX" } Response: { "ok": true, "email": { "to": "...", "subject": "...", "body_html": "...", "body_text": "..." } } Always returns ok=true even if email unknown (prevent enumeration). Only returns email payload when the user actually exists.

Parameters

token e.g. {TOKEN}",

IP allowlist: restricted to CIDR blocks in HHW_INTERNAL_JWKS_ALLOWED_IPS env. Leave unset in dev to allow all. Production should pin to tenant server IPs.

Body: {"email": "...", "password": "...", "ip": "<optional end-user IP>", "user_agent": "..."} Response: {"jwt": "...", "user": {...}} or {"error": "..."}

Body: { "provider": "google", "provider_sub": "1234567890", "email": "...", "email_verified": true, "name": "...", "given_name": "...", "family_name": "...", "picture": "...", "locale": "..." }

Body: { "state_token": "<64-hex>", "code_verifier": "<64-hex>", "return_url": "/members", "redirect_uri": "https://1vsm.com/api/auth/google-callback.php" }

Body: { "user_id": 123, "verify_url_template": "https://bgpdx.com/verify-email?token={TOKEN}", "site_name": "Bamboo Grove PDX" }

Parameters

token e.g. {TOKEN}",

per requireInternalTenantAuth(). Only registered + active tenants can consume tokens. Request body: {"token": "<sso_token>"} token = 32-128 hex Response: 200 {ok:true, user: {id, username, email, display_name, avatar_url, is_admin}} 400 {ok:false, error:'invalid_format'} 401 {ok:false, error:'invalid_or_expired_token'} 401 {ok:false, error:'origin_mismatch'} 401 {ok:false, error:'user_disabled'} 500 {ok:false, error:'server_error'} Single-use: a token consumed by this endpoint cannot be consumed again (atomic UPDATE…WHERE used_at IS NULL guarantees one winner across racers).

Body: {"jwt": "..."}

Body: {"token": "...", "new_password": "..."}

&category=&city=&entity_type=&sort=newest&limit=24&offset=0&featured_only=0 Returns entities published to the requesting site. Used by satellite sites (Oregon Tires, Hoku Events, etc.) to pull directory listings.

Parameters

site_key e.g. oregon_tires (required)

Returns all publication records for an entity (which sites it's published to, featured status, publish dates).

Parameters

entity_id e.g. 123 (required)

Closed proxy: takes ids only, never an arbitrary URL. Prevents abuse.

?username=mozaycalloway Response: { success: true, artist: { username, display_name, avatar_url }, responses: [{ question_id, question_text, category, response_text, published_at }, ...] }

Parameters

username e.g. mozaycalloway

Body (JSON): { "question_id": "origin" } Response: { success: true, is_published: true/false }

?key=default (optional, defaults to 'default') Response: { success: true, interview: { id, title, description, questions: [...] } }

Parameters

key e.g. default

Body (JSON): { "interview_key": "default", "question_id": "origin", "response_text": "..." } Response: { success: true }

Writes to hd_entities (entity_type='venue') + hd_venues with moderation_status='pending'. Admin approves via /admin/?page=queue.

Parameters

page e.g. queue.