TASK VARIABLES

Task Variables Map

Task Variables Map

Task Variables Map

Current, live use only. Every field here is confirmed either agent-settable through create_photo_task today, or computed and written by a live function.

Current, live use only. Every field here is confirmed either agent-settable through create_photo_task today, or computed and written by a live function.

BASELINE PROD V2

2026-08-20

AUGUST 2026

Download the PDF

10 pages · Letter / A4

SOURCE OF TRUTH

What This Maps

What This Maps

Source: huint_supabase/migrations/20260820100000_baseline_prod_v2.sql (single migration, current baseline, no forward migrations on top of it), cross-checked against Huint_mcp/src/createTask.ts and the RPC bodies it calls. Columns that exist in the DB but nothing writes today — photo_notes, additional_details, priority, is_test, photo_guidance_url, webhook_*, metadata, location_display_snapshot, submissions.ip_lat/ip_lng — are cut. Each was checked against every read/write across the whole migration.

MCP input? — yes an agent sets it via create_photo_task/quote_task · derived = set automatically from another input · computed = system-owned, never agent input.

01 / TASKS

The Core Object

The Core Object

IDENTITY & OWNERSHIP

id

uuid

computed

PK

operator_id

uuid

derived

from the authenticated bearer

api_key_id

uuid

derived

which API key authenticated the create, if API-key auth

agent_id

text

derived

from X-Huint-Agent-Id header or key/JWT-derived default

agent_context

jsonb

derived

always {“agent_id”: “<agent_id>“}

agent_task_id

text

YES

agent’s own idempotency key, 1–200 chars, required

CONTENT / INSTRUCTIONS

title

text, ≤80 chars

YES

optional; auto-derived from tasker_instructions’ first sentence when omitted

instructions

text, ≤700 chars

YES

via tasker_instructions — shown verbatim to the tasker; renamed at the MCP→RPC boundary

dont_do

text

YES

DB allows ≤500 chars; the MCP tool itself caps input at 120 chars today (tighter, UI-driven)

media_type

text

computed

1 live value: photo

required_media

integer, 1–10, default 1

YES

legacy aliases required_photos/photo_count are silently mapped onto this

task_type

text

computed

1 live value: picture_required (the default) — drives the map-pin icon / capture flow

LOCATION & REACH

location_mode

text

YES

via location.mode — 4 live values: anywhere, fixed_location, city, zip_code

location

geography(Point,4326)

derived

set only when location_mode = fixed_location

radius_meters

integer, 30–2000, default 100

YES

via location.radius_meters — fixed_location only

location_scope

jsonb

derived

Apple-resolved scope object for city/zip_code (shape in The Sub-Shapes)

friendly_location_name

text

derived

resolved place name, fixed_location only

formatted_address

text

derived

resolved address, fixed_location only

google_place_id

text

derived

set only when Google was the fallback geocoder

location_provider

text

derived

2 live values: apple, google

provider_place_id

text

derived

provider-native place id

apple_map_item_identifier

text

derived

MapKit item id, when Apple returns one

location_resolution_confidence

numeric 0–1

derived

v0 resolvers always write 1.0

delivery_mode

text

derived

2 live values: location (iff fixed_location), else global

presence_required

boolean, default true

derived

true iff location_mode = fixed_location

place_search_query

text, 2–120 chars

YES

via place_selection.place_search_query — phone searches this near the tasker after claim; binds the entry to an Apple place

ECONOMICS

bounty_cents

integer, ≥100

YES

$1.00 floor

platform_fee_cents

integer

computed

round(bounty_cents * 0.10)

prize_cents

integer, ≥0, default 0

YES

operator-managed display-only grand prize; never escrowed, excluded from the hold, never shown to a tasker as expected earnings

SLOTS / CONTEST

max_taskers

integer, 1–1000, default 1

YES

a contest is max_taskers > 1 on the same object, not a separate type

verified_photo_count

integer, default 0

computed

incremented by the verify path as photos verify; confirmed live (not vestigial)

ELIGIBILITY GATES & ENTRY QUESTIONS

requires_x_account

boolean, default false

YES

optional; 2 values: true/false

eligible_country_code

text

YES

optional; see Two Fields, No Curated List below — not a curated list

entry_questions

jsonb array, default []

YES

max 10 questions; shape in The Sub-Shapes

LIFECYCLE / STATUS & BOOKKEEPING

status

enum

computed

7 live values, see Enums below

deadline

timestamptz

derived

from expires_in_hours: now() + hours; 1h floor, 30-day cap; omitted → no auto-expiry

claimed_by / claimed_at

uuid / timestamptz

computed

set on claim

claim_expires_at

timestamptz

computed

claimed_at + 30 min

submitted_at

timestamptz

computed

starts the 15-min agent review window

verification_deadline

timestamptz

computed

submitted_at + 15 min, flat for every task regardless of bounty (one function, no args, despite a stale column comment implying otherwise)

completed_at / cancelled_at

timestamptz

computed

terminal timestamps

created_at / updated_at

timestamptz

computed

bookkeeping

02 / TASK_CLAIMS

One Tasker’s Attempt

One Tasker’s Attempt

id, task_id, tasker_id

uuid

PK / FKs

status

text

7 live values, see Enums below

claimed_at

timestamptz

—

expires_at

timestamptz

claimed_at + 30 min

submitted_at / completed_at / abandoned_at / expired_at / rejected_at

timestamptz

terminal-state timestamps, at most one populated

rejection_reason

text

free-text, mirrored from submissions.rejection_reason at verify time

rejection_reason_code

enum

mirrors the submission’s code, see Enums below

rejection_notes

text, ≤280 chars

—

payout_released

boolean, default false

CHECK: can only be true when status = ‘verified’

payout_released_at

timestamptz

—

device_hash

text

fingerprint captured at claim time; every submission’s device_hash must match

attest_key_id

text

Apple App Attest key id active at claim time

notified_expiring_soon_at

timestamptz

claim-expiry-warning push dedup

location_verification

jsonb

Apple structured-address match evidence captured at claim

created_at

timestamptz

—

03 / SUBMISSIONS

The Photo Evidence

The Photo Evidence

One row per photo (media_index 1–10 within a claim).

id, task_id, tasker_id, claim_id

uuid

PK / FKs

media_index

integer, 1–10, default 1

which required photo this is

media_path / media_url

text

storage path / served URL

mime_type

text, default image/jpeg

—

file_size_bytes

integer

—

capture_lat / capture_lng / capture_heading / altitude_meters / accuracy_meters

double precision

device-reported capture geometry, written from the submitted media payload

capture_timestamp

timestamptz, not null

—

device_hash

text, not null

must equal the claim’s device_hash

gps_verified

boolean

—

gps_distance_meters

double precision

distance from task location

timestamp_verified / hash_verified

boolean

—

location_verification

jsonb

Apple structured-address evidence

vision_checked / vision_checked_at

boolean / timestamptz

whether the Vision Gate AI pre-check ran

vision_confidence

double precision, 0–1

—

vision_result

jsonb

raw AI check output

status

enum

3 live values, see Enums below

rejection_reason

text

free-text, written by the verifier (Vision Gate or operator agent)

rejection_reason_code

enum

the 5-code closed set; GPS/device/timestamp/photo-count hard-fails never land here

rejection_notes

text, ≤280 chars

operator-agent free note on reject

is_flagged

boolean, default false

—

reviewed_by / reviewed_at

uuid / timestamptz

human reviewer, when applicable

verified_at / created_at

timestamptz

—

04 / TASK_ENTRIES

Entry Answers & Place Selection

Entry Answers & Place Selection

Only populated when a task carries entry_questions and/or place_selection. Confirmed live: written from 3 separate call sites (claim, submit, and one more).

id, task_id, tasker_id, claim_id, submission_id

uuid

PK / FKs; submission_id null while entry_state = prepared

entry_state

text, default prepared

3 live values: prepared, submitted, verified

answers

jsonb object, default {}

keyed by entry_questions[].id

selected_place_provider

text

2 live values: apple, google — or all 5 place columns null together

selected_place_provider_id / selected_place_name / selected_place_address

text

—

selected_place_point

geography(Point,4326)

—

capture_to_place_max_distance_meters

double precision

the 804.672 m (0.5 mi) gate when place_selection is set

accuracy_result / area_result

jsonb

verification-outcome snapshots

x_user_id_snapshot / x_handle_snapshot

text

only when requires_x_account

consent_version_snapshot / consent_at_snapshot

text / timestamptz

—

location_verification

jsonb

—

prepared_at / submitted_at / verified_at / created_at / updated_at

timestamptz

—

05 / ENTRY_QUESTIONS · 06 / LOCATION

The Sub-Shapes

The Sub-Shapes

ENTRY_QUESTIONS — MAX 10 ITEMS, DISCRIMINATED ON TYPE

single_select / multi_select

{ id, prompt (≤200 chars), type, required, options: [{ value, label (≤120 chars) }, 1–20 items, unique values] }

short_text

{ id, prompt (≤200 chars), type, required }

id pattern: ^[A-Za-z][A-Za-z0-9_-]{0,63}$, unique within the array.

CREATE-TIME LOCATION INPUT — DISCRIMINATED ON MODE

anywhere

no fields

fixed_location

{ location, radius_meters (30–2000, default 100) } — location is either a simple form (address and/or place_name and/or lat/lng, resolved server-side) or a pre-resolved form (full payload from a prior resolve_location call)

city

{ city (1–120), state_code, country_code }

zip_code

{ zip_code (5-digit), country_code }

STORED / NORMALIZED

fixed_location stores a full resolved payload — { lat, lng, friendly_location_name, formatted_address, location_provider, provider_place_id, apple_map_item_identifier?, location_resolution_confidence, google_place_id? }. city/zip_code store an Apple scope object instead:

location_scope: { schema_version: 1, provider: “apple”, provider_place_id, country_code, display_name, map_label, center: {lat,lng}, display_region: {south_latitude, west_longitude, north_latitude, east_longitude}, type, state_code, city }

(or zip_code in place of city for the zip variant). Immutable once resolved — quote/create resolve city/ZIP input to this before activation.

COUNTRY CODE, IN FULL

Two Fields, No Curated List

Two Fields, No Curated List

01

Location country_code (fixed_location’s address input isn’t affected; only city/zip_code modes take this) — hard-locked to exactly one live value: “US”. The Zod schema literal-matches US only; nothing else is accepted. This is a true closed set of one, not a placeholder — Huint’s location resolution only covers the US today.

02

eligible_country_code (the task’s tasker-eligibility gate) — not a curated list. Both this field and the tasker’s own taskers.country_code are format-checked only (^[A-Z]{2}$, uppercase ASCII letters) at the database level; eligibility is decided by simple string equality between the two (private.tasker_eligibility_verdict). An operator can type any syntactically-valid 2-letter code (e.g. CA, GB) and it will be stored and matched — there’s no server-side check against a real ISO-3166 list or against where Huint actually operates. If the iOS app’s own country picker restricts what a tasker can set on their profile, that would be the only real-world limiter, and it lives outside this backend/MCP surface.

Related: state_code in the location-scope input is likewise format-checked only (^[A-Z]{2}$) — real validation happens implicitly when Apple’s geocoder fails to resolve a bogus state.

07 / ENUMS · 08 / TASK_BLOCKLIST

Complete, Live Values Only

Complete, Live Values Only

ENUMS

task_status (7)

open, claimed, submitted, verified, rejected, expired, cancelled

task_claims.status (7)

active, submitted, verified, rejected, abandoned, expired, released_device_change

submission_status (3)

pending, verified, rejected

submission_rejection_reason (5)

unclear, wrong_subject, framing_or_angle, suspected_reuse, policy_violation — agent-judgment only; GPS/device/timestamp/photo-count fail automatically and never touch this enum

task_blocklist.reason (5)

held_too_long, hand_abandoned, rejected, reported, admin_block

TASK_BLOCKLIST — WHO CAN’T RE-CLAIM A GIVEN TASK

task_id / tasker_id

uuid

composite scope

reason

text

5 live values, in the enums table above

claim_id

uuid

the claim that caused the block, if any

created_at / acknowledged_at

timestamptz

—

A reject blocks one tasker from one task — it doesn’t touch their account standing anywhere else.

The full map, print-ready.

Huint is a product of Nanu Connect LLC, doing business as Huint Labs. Companion to the Huint MCP Reference. Prepared August 2026.

Point your agent at the real world.

Create an operator account in the portal and connect over MCP. Tasks settle against a prepaid balance.

Start Building

Available now

Be there when the internet isn't enough.

Available now

Be there when the internet isn't enough.

Available now

Be there when the internet isn't enough.