# Your first activity. Go from an original FIT file to decoded JSON and a verified copy of the source. This example runs on your computer or server with Node.js 22 or later. No packages are required. ## Before you begin You need access to an enabled MovePacket beta environment, an API key for that environment, and a FIT activity file no larger than 4 MiB. Keep the key in your secret manager or server environment as `MOVEPACKET_API_KEY`. Never put it in browser code or a URL. For a test without personal data, [download the synthetic sample ride](/sample.fit). It contains generated cycling data, not a real athlete's ride. ## Run the complete example [Download movepacket-quickstart.mjs](/examples/movepacket-quickstart.mjs) and save it beside your FIT file. Set the API origin supplied with your beta access. For the local development preview, use `http://127.0.0.1:8787`. ```sh export MOVEPACKET_API_URL="https://movepacket.com" # Supply MOVEPACKET_API_KEY through your secret manager or environment. node movepacket-quickstart.mjs ride.fit ./ride-data ``` Choose an output directory that does not already exist. The example uploads the file, polls for completion, downloads the decoded data and original, and compares the original's SHA-256 against both your input and the API checksum. It saves `activity.json` and `original.fit` only after verification. If processing takes more than two minutes, the example returns the activity ID so you can check it again. A local timeout does not cancel processing. Transient request failures are retried up to three attempts. Re-uploading identical bytes within the same workspace reuses the activity. ## Upload the file You can also follow the flow one request at a time. Send the raw file body, not a multipart form or JSON wrapper. ```sh curl --fail-with-body "$MOVEPACKET_API_URL/v1/activities" \ -H "Authorization: Bearer $MOVEPACKET_API_KEY" \ -H "Content-Type: application/vnd.ant.fit" \ --data-binary @ride.fit ``` An accepted upload normally returns HTTP `202`. Save its `id`. Responses include links to the status, original, and decoded data. The IDs and checksum below are illustrative. ```json { "id": "act_00000000-0000-4000-8000-000000000001", "status": "queued", "sha256": "0000000000000000000000000000000000000000000000000000000000000000", "byte_size": 25000, "error_code": null, "created_at": "2026-09-16T12:00:00.000Z", "duplicate": false, "links": { "self": "/v1/activities/act_00000000-0000-4000-8000-000000000001", "original": "/v1/activities/act_00000000-0000-4000-8000-000000000001/original", "data": "/v1/activities/act_00000000-0000-4000-8000-000000000001/data" } } ``` ## Wait for ready Use the returned ID as `ACTIVITY_ID`. Poll this endpoint every few seconds until `status` is `ready` or `failed`. ```sh curl --fail-with-body "$MOVEPACKET_API_URL/v1/activities/$ACTIVITY_ID" \ -H "Authorization: Bearer $MOVEPACKET_API_KEY" ``` A successful status request returns HTTP `200` even while processing continues. The `status` field determines whether the decoded data is ready. A failed activity includes an `error_code`; its original remains retrievable. ## Retrieve the result ```sh curl --fail-with-body "$MOVEPACKET_API_URL/v1/activities/$ACTIVITY_ID/data" \ -H "Authorization: Bearer $MOVEPACKET_API_KEY" \ --output activity.json curl --fail-with-body "$MOVEPACKET_API_URL/v1/activities/$ACTIVITY_ID/original" \ -H "Authorization: Bearer $MOVEPACKET_API_KEY" \ --output original.fit ``` The complete Node.js example verifies file integrity automatically. For your own integration, compare the downloaded original's SHA-256 with the upload response and your source file. Continue with [processing and reliability](/docs/processing) and the [data model](/docs/data-model).