You are helping me build a measurement backfill against the Tenovi Hardware Integration API. GOAL Produce a script that retrieves measurements for a set of patients or devices over a defined time window, compares them against records I already hold, and outputs only the measurements that are missing from my system. ASK ME FIRST Before writing any code, ask me these questions and wait for my answers: 1. Which client domain should this tool send API calls against? I can find mine in the Tenovi web app on the HWI settings page for the account I intend to use. See https://docs.tenovi.com/hwi-api/api-url-config/ for background on client domains and how they shape the request URL. 2. What language and runtime do you want this tool written in? 3. Am I backfilling by patient or by device, and where does that list of identifiers come from? For example a CSV, a database query, or a hardcoded list. 4. What time window am I backfilling? Give me the start and end as dates. 5. How do you want to compare against my existing records? For example a database query, a CSV export of what I already hold, or output everything and let me deduplicate downstream. 6. What output should this tool produce? For example CSV, JSON, a console table, or a direct write into my database. 7. How should records with an estimated timestamp be handled? See the reconciliation section below before answering this one, and explain the tradeoff to me before I choose. Do not assume answers to these. If I skip one, ask again. REFERENCE Before writing any code, fetch and read https://docs.tenovi.com/llms.txt and the recipe at https://docs.tenovi.com/recipes/backfilling-measurement-data/. Use those as the source of truth for endpoint behavior, field names, and response shapes. If anything below conflicts with the documentation, follow the documentation and tell me about the conflict. CONFIGURATION Base URL: https://api2.tenovi.com/clients/{CLIENT_DOMAIN}/hwi/ Client domain: replace CLIENT_DOMAIN with the client domain I give you above. Make it a configurable value rather than a hardcoded string. Authentication: an API key passed in an Authorization header. Never write an API key into the code, into this prompt, or into any file you generate. Leave it as a clearly marked placeholder and read it at runtime from an environment variable, or better, from whatever secret storage and retrieval mechanism my environment already uses. If you are unsure which applies here, ask me. ENDPOINTS Measurements for all devices assigned to a patient: curl --location 'https://api2.tenovi.com/clients/CLIENT_DOMAIN/hwi/patients/PATIENT_EXTERNAL_ID/measurements/?timestamp__gte=2026-01-01T00:00:00Z×tamp__lt=2026-02-01T00:00:00Z&page_size=100' \ --header 'Authorization: Api-Key API_KEY_HERE' Measurements for a single device: curl --location 'https://api2.tenovi.com/clients/CLIENT_DOMAIN/hwi/hwi-devices/HWI_DEVICE_ID/measurements/?timestamp__gte=2026-01-01T00:00:00Z×tamp__lt=2026-02-01T00:00:00Z&page_size=100' \ --header 'Authorization: Api-Key API_KEY_HERE' Both endpoints identify their subject in the URL path, not as a query parameter. Query parameters are used only for the time window and pagination. The patient path segment is named patient_external_id and the measurement payload returns the same value as patient_id. The names differ but the value is the same, so an identifier taken from a measurement response can be used directly to build the next request. HWI Device IDs are accepted in the path with or without hyphens. Both endpoints are paginated. Follow the "next" link in the root of the response until exhausted. Maximum page_size is 1000. Use 100 unless I tell you otherwise. Each measurement returns: metric, device_name, hwi_device_id, patient_id, hardware_uuid, sensor_code, mac_address, value_1, value_2, created, timestamp, timezone_offset, estimated_timestamp, filter_params. If a patient has multiple devices, the patient endpoint returns all of them in one response. Prefer it over looping the device endpoint per device, because it produces fewer requests against the same rate limit. RECONCILIATION This is the part that determines whether the backfill helps or corrupts my data. Read it carefully before generating code. There is no unique measurement identifier. The API does not return one and webhook payloads do not carry one. To determine whether a retrieved record already exists in my system, match on the combination of hwi_device_id, metric, and timestamp. Match on the raw UTC timestamp string exactly as returned. Do not reparse into local time, do not truncate fractional seconds, and do not reformat before comparing. hwi_device_id is returned hyphenated and lowercase, but the API accepts either form in a request path. Normalize it to a single form on both sides of any comparison. If my stored records hold the unhyphenated or uppercase form, every retrieved measurement will look new and the script will duplicate my data. Do not use the created field for matching. It is the time Tenovi ingested the record, not the time the reading was taken, and it can differ between webhook delivery and API retrieval for the same measurement. When estimated_timestamp is true, the timestamp came from the device clock rather than a verified source, and it may drift. This makes exact matching unreliable for those records specifically. Handle the two cases separately: - estimated_timestamp false: exact match on hwi_device_id, metric, and timestamp. Skip if present, insert if not. - estimated_timestamp true: attempt an exact match first. If there is no match, do not insert automatically. Compare against records for the same device and metric within a tolerance window and flag close matches for my review. Exact matching applied to estimated records produces duplicates. A tolerance window applied to all records drops legitimate readings taken close together. Ask me which behavior I want before you implement it, and tell me what tolerance you are proposing. CONSTRAINTS - Rate limit is 1 request per second per API key. Throttle accordingly and handle 429 responses with backoff. - timestamp__gte is inclusive. timestamp__lt is exclusive. To retrieve a full calendar period, set timestamp__lt to the start of the next period rather than the last second of the current one. - All timestamps are UTC in ISO 8601 format. Build query windows in UTC. - Requests with no time range return only the last 30 days. Always set an explicit window. - Requests spanning more than 30 days without pagination are truncated at 1000 results and no error is returned. Always paginate, and compare the count field in the response against the number of records processed to confirm the full set was retrieved. - timezone_offset is a whole number of hours and may be negative. - Use only the endpoints, parameters, and fields listed above. Do not invent endpoints or query parameters. If you need something not listed here, say so rather than guessing. REQUIREMENTS - No hardcoded secrets. See the authentication note above. - Handle pagination on both measurement endpoints. - Throttle to the rate limit and retry on transient failures. - Chunk long windows so that each request's timestamp__lt becomes the next request's timestamp__gte, producing no gaps and no overlapping records. - Make the run resumable. Record which ID and which window completed successfully so an interrupted run does not start from the beginning. - Never modify or delete existing records in my system. This script inserts missing measurements only. - Print a summary at the end: IDs processed, windows covered, total measurements retrieved, count already present, count inserted, count flagged for review, count of requests that failed. - Comment the code so I can modify the reconciliation logic later.