goals

Take the goal this token names into the authenticated account (#2566).

The one Firebase route on this surface, and the split is the point. Every other /goals route answers an anonymous browser holding a cookie, because an athlete settles a goal before they have an account (#1851); this one answers the app, which holds a bearer and none of the browser's three signals — no cookie, no CSRF token (a browser concept), no Origin. The token proves I was handed this goal's link; the bearer proves I am this athlete. Neither alone is enough, which is the channel-link security model (#858) with the goal in place of the channel identity.

First claim wins while there is still an account to lose it to: a second bearer gets 409 when the goal's holder can still be found — it holds a provider link or an email — and a holder that can be found by neither is a dead install, so the goal is handed back instead (#2624 D6, :func:_handed_back). The same account claiming again is a success with claimed_at unmoved, because the app may resend a confirm whose answer it never saw.

Answers with the owner's detail payload, so the app draws the goal from the claim instead of following it with a read — and reads it in the shape looking-glass already renders, plus recovered and the custom_token a re-hand has to sign in with.

It is also the one route here that can create a User, because the athlete it answers may not have one yet; :func:_claiming_account carries that and the reasoning behind it. That is why the token is read first and the row is minted second, which inverts what this handler used to do: a claim that cannot land must not leave an account behind it, so an unknown token and a goal already held both answer before anything is created. The cost is one indexed read on a route an install runs once.

The phone is written down when the claim lands (#2637), for the same reason the settle writes it: taking a goal into this account on this device is the athlete committing to it here. After the claim, so a refused one leaves no key behind either.

A free goal on a phone we already know lands on the phone's account (#2952). An athlete who deletes the app, settles a new goal on the web and installs from its button arrives on a fresh uid holding the link, and the key is the only thing that says who they are. Claiming onto the fresh uid minted a second account and then repointed the key at it, so the first account and its goal were left where nothing would ever reach them. The claim now makes the bootstrap's recognition (:func:_recognized_phone, the same rule and the same refusals) before it stamps the goal, and answers recovered with the token the phone signs in with. The same phone asking again, once the goal is on the key's account, gets the same answer, so a lost response or a failed fold is a retry away. A goal someone else holds, a bearer's own replay, a key nobody holds and a key the bearer already holds all claim as before.

post/goals/claim

Request

  • Base URL
    https://api.imperfect.co (the document declares no server; this is the origin it was published from)
  • URL
    https://api.imperfect.co/goals/claim
  • Auth
    HTTP bearer

Headers

x-device-timezonestring nullable
x-device-hintstring nullable
x-client-platformstring nullable

Request body

tokenstring required

Response

Successful Response

namestring required
linestring nullable
localestring required
kind'destination' | 'practice' required
sportstring nullable
set_atstring date-time required
anchor_kind'event' | 'route' nullable
factorsobject[]
page_expected_untilstring date-time nullable
goal_idstring required
status'exploring' | 'proposing' | 'settled' required
share_idstring nullable
slugstring nullable
published_atstring date-time nullable
claimed_atstring date-time nullable
recoveredboolean
custom_tokenstring nullable

Changes

    • warning

      removed the optional property // from the response with the status

    • info

      added the optional property // to the response with the status

    • info

      added the optional property to the response with the status