Array Data Uploads Integration Guide¶
Feature Availability
The Array Data Uploads feature is enabled on demand. If you're interested in using this feature to enable direct-to-consumer array-data analysis through your application, please contact Gencove support to have it enabled for your organization.
Overview¶
The Array Data Uploads integration allows your application to submit 23andMe, Ancestry, or MyHeritage raw genotyping exports to Gencove through the API. Your application collects the file from your end-user, requests a short-lived upload authorization from Gencove, and posts the file directly to Gencove storage.
Prefer not to build the upload form?
If you'd rather have your end-users pick and upload their own file on a Gencove-hosted page instead of collecting it in your application, see Gencove-Hosted Array Data Upload Form below.
Key Concepts¶
Before integrating, it's helpful to understand a few core Gencove concepts:
- Sample: Represents a single genetic sample in Gencove. In the array-data upload workflow, one upload equals one sample. Each sample has a unique ID and tracks the entire lifecycle from upload request through analysis.
- Sample Status: Samples progress through various states (e.g.,
array data waiting for upload,array data upload expired,scheduled,running,succeeded). - Project: A container for organizing related samples in Gencove. By default, samples are created in a project Gencove provisions for your organization; you can optionally target a specific project by passing its
project_idin the request.
Accepted uploads¶
Your end-user must supply an unmodified .zip export from 23andMe, Ancestry, or MyHeritage. The maximum file size is 25 MB. Other file types and oversized files are rejected.
How It Works¶
The array-data upload workflow involves three key parties:
- Your end-user: The person supplying their genotyping export
- Your application (consumer health company): Collects the file, integrates with Gencove's API, and uploads the file
- Gencove: Stores the file and runs analysis
Your end-user never contacts Gencove directly. Your application holds the file and performs the upload on their behalf, using an upload authorization issued by the Gencove API.
sequenceDiagram
autonumber
actor EU as End-user
participant CC as Consumer company
participant API as Gencove API
participant GC as Gencove
EU->>CC: Provides array data export
CC->>CC: Accept file, store information
Note over CC,API: CC uses Gencove API key for the request
CC->>API: POST https://api.gencove.com/api/v2/array-data-upload/
API-->>CC: Return sample ID, upload URL and upload fields
CC->>GC: POST .zip file (upload fields first, file part last)
GC-->>CC: 204 No Content
GC->>GC: Schedule sample and process array data
alt Delivery back to CC via webhook
GC-->>CC: POST results (webhook)
CC-->>EU: Display results in application
else Delivery back to CC via API pull
CC->>API: GET https://api.gencove.com/api/v2/samples/{sample_id}
API-->>CC: Sample status and deliverables
CC-->>EU: Display results in application
end
sequenceDiagram
autonumber
actor EU as End-user
participant CC as Consumer company
participant API as Gencove API
participant GC as Gencove
EU->>CC: Provides array data export
CC->>CC: Accept file, store information
Note over CC,API: CC uses Gencove API key for the request
CC->>API: POST https://api.gencove.com/api/v2/array-data-upload/
API-->>CC: Return sample ID, upload URL and upload fields
CC->>GC: POST .zip file (upload fields first, file part last)
GC-->>CC: 204 No Content
GC->>GC: Schedule sample and process array data
alt Delivery back to CC via webhook
GC-->>CC: POST results (webhook)
CC-->>EU: Display results in application
else Delivery back to CC via API pull
CC->>API: GET https://api.gencove.com/api/v2/samples/{sample_id}
API-->>CC: Sample status and deliverables
CC-->>EU: Display results in application
end
Workflow Steps¶
- File collected: Your end-user supplies their 23andMe, Ancestry, or MyHeritage
.zipexport in your application - Accept request: Your application accepts the file and stores necessary information
- Request an upload: Your application makes an authenticated request to Gencove's API to request an upload
- Sample ID and authorization: Gencove returns a unique sample ID, an upload URL, and the form fields that authorize the upload
- Upload the file: Your application posts the
.zipfile to that URL, echoing back the returned fields with the file part last - Gencove analysis: Gencove stores the file, schedules the sample, and runs analysis on the array data
- Results delivery via webhook: (RECOMMENDED) Gencove delivers results to your application via webhook when they're ready
- Fetch results via API: Periodically ping the Gencove API to get sample status and results
- Display results: Your application displays the results to your end-user
Integration Guide¶
Get Your API Key¶
To make requests to Gencove's API, you'll need an API key. This key authenticates your application and should be kept secure (treat it like a secret or a password).
To generate an API key:
- Log in to your Gencove account at web.gencove.com
- In the left sidebar, click on Account
- Navigate to the API Keys tab
- Click the Generate new API key button
- Copy the generated API key and store it securely
Security Best Practice: For production use, consider creating a separate Gencove user with
Analystlevel privileges specifically for API access, and use that user's API key. This follows the principle of least privilege and makes it easier to manage access if needed.
Keep Your API Key Secret
Your API key provides access to your Gencove account. Never commit it to version control, never share it publicly, never expose it in client-side code. Store it in a secure secrets management system. If your key gets exposed, make sure to revoke it immediately and generate a new one.
Create a Gencove Project¶
Before uploading array data, you need a project for array-data samples.
Gencove support should have created a properly configured project during onboarding; if not, please contact our team.
If you organize samples across more than one array-data project, you can direct each upload to a specific project by passing its project_id when you request the upload (see Request an Upload). When you omit project_id, samples land in the default array-data project provisioned for your organization.
Request an Upload¶
When you have the end-user's file in hand, your application should make a POST request to Gencove's array-data upload endpoint. Gencove creates the sample and returns a short-lived authorization to upload one file for it.
API Endpoint: POST https://api.gencove.com/api/v2/array-data-upload/
Authentication: Include your API key in the Authorization header: Authorization: Api-Key $GENCOVE_API_KEY
Request Body:
{
"project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"metadata": {
"customer_id": "cust_12345"
}
}
Optional Fields:
| Field | Description |
|---|---|
project_id |
UUID of the project the sample should be created in. The project must belong to your organization, be unlocked, and be configured for array data. If omitted, Gencove uses the default array-data project provisioned for your organization. |
metadata |
Free-form JSON object stored on the sample (useful for linking back to your system). See Sample metadata and files |
Both fields are optional; an empty body is valid.
Response:
{
"sample_id": "6c3437b4-35fc-4224-9bb0-4623a16e6735",
"upload_url": "https://v2-api-files-prod.s3.amazonaws.com/",
"upload_fields": {
"Content-Type": "application/zip",
"key": "organization/.../uploads/....zip",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-credential": ".../20260727/us-east-1/s3/aws4_request",
"x-amz-date": "20260727T120000Z",
"x-amz-security-token": "...",
"policy": "eyJleHBpcmF0aW9uI...",
"x-amz-signature": "6de44e..."
},
"file_field_name": "file",
"expires_at": "2026-07-27T12:15:00Z",
"max_file_size_bytes": 26214400,
"content_type": "application/zip"
}
| Field | Description |
|---|---|
sample_id |
UUID of the sample created for this upload |
upload_url |
The URL your application posts the file to |
upload_fields |
Form fields that authorize the upload. Send them back exactly as received — see Upload the File |
file_field_name |
Name of the multipart field that carries the file itself |
expires_at |
When the upload authorization expires, 15 minutes after the request |
max_file_size_bytes |
Largest accepted file size in bytes (26214400, i.e. 25 MB) |
content_type |
Content type the upload must declare — always application/zip, and already prefilled in upload_fields |
Important: Store the returned sample_id in your database. This is the unique identifier you'll use to track the sample's status and retrieve results. You can also find samples later using values in metadata — see Sample metadata and files.
The upload authorization is valid for 15 minutes. Request the upload immediately before you send the file, not ahead of time.
There is no test mode on this endpoint, and no way to refresh an authorization. If it lapses or the upload fails, call the endpoint again — that creates a new sample with a new authorization.
Upload the File¶
Post the .zip file to upload_url as a multipart/form-data request. This request does not carry your API key; the fields in upload_fields are what authorize it.
Two rules govern the request body:
- Echo
upload_fieldsback verbatim. Iterate over whatever keys you received and send each one as a form field with the value you were given. The exact set of keys is controlled by the storage layer and can change without notice, so never hardcode the list. - Put the file part last. Everything after the file part in the body is ignored, so any authorization field that follows the file is treated as missing and the upload is rejected with
403 Forbidden.
The file part must come last
Sending the file before the fields from upload_fields is the most common integration mistake, and it fails with 403 Forbidden rather than a descriptive error. Append the file part after every other field.
On success the upload returns HTTP 204 with an empty body. That is the expected result, not an error.
# 1. Request an upload
curl -X POST "https://api.gencove.com/api/v2/array-data-upload/" \
-H "Authorization: Api-Key $GENCOVE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"metadata": {"customer_id": "cust_12345"}
}'
# 2. Upload the file, echoing back every key from upload_fields,
# with the file part LAST
curl -X POST "https://v2-api-files-prod.s3.amazonaws.com/" \
-F "Content-Type=application/zip" \
-F "key=organization/.../uploads/....zip" \
-F "x-amz-algorithm=AWS4-HMAC-SHA256" \
-F "x-amz-credential=.../20260727/us-east-1/s3/aws4_request" \
-F "x-amz-date=20260727T120000Z" \
-F "x-amz-security-token=..." \
-F "policy=eyJleHBpcmF0aW9uI..." \
-F "x-amz-signature=6de44e..." \
-F "file=@23andme_export.zip"
import os
import requests
API_KEY = os.getenv("GENCOVE_API_KEY")
headers = {
"Authorization": f"Api-Key {API_KEY}",
"Content-Type": "application/json",
}
payload = {
"project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"metadata": {"customer_id": "cust_12345"},
}
response = requests.post(
"https://api.gencove.com/api/v2/array-data-upload/",
headers=headers,
json=payload,
)
response.raise_for_status()
upload = response.json()
print(f"Sample ID: {upload['sample_id']}")
# requests writes the `data` fields first and the `files` part last,
# which is exactly the ordering the upload requires
with open("23andme_export.zip", "rb") as handle:
upload_response = requests.post(
upload["upload_url"],
data=upload["upload_fields"],
files={upload["file_field_name"]: handle},
)
upload_response.raise_for_status() # 204 No Content on success
With requests, pass upload_fields as data and the file as files. The library serializes data before files, so the file part ends up last without any extra work. If you use a different HTTP client, check that it preserves that ordering.
Once the file lands, the sample is scheduled for analysis automatically. You do not need to make a separate API call to start processing.
Tracking Sample Status¶
Relevant statuses for array-data upload samples:
| Status | Meaning |
|---|---|
array data waiting for upload |
Upload requested; waiting for the file |
array data upload expired |
The upload authorization lapsed before a file arrived |
scheduled |
File received; waiting to start analysis |
running |
Analysis in progress |
succeeded |
Analysis complete; deliverables available |
| Failed statuses | Analysis or QC failure (same patterns as other samples) |
We recommend using event notifications to learn when a sample reaches succeeded. Alternatively, poll GET https://api.gencove.com/api/v2/samples/{sample_id}/.
Simplified response:
{
"id": "6c3437b4-35fc-4224-9bb0-4623a16e6735",
"client_id": "",
"last_status": {
"status": "running",
"created": "2026-07-27T12:34:56Z"
},
"project": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"metadata": {
"customer_id": "cust_12345"
}
}
Note: If using polling, check sample status periodically rather than on every page load. Analysis turnaround time depends on queue load and is typically shorter than kit-based workflows.
Receive Results¶
Once analysis is complete, you need to retrieve the results. Gencove offers two approaches:
Option 1: Webhooks (Recommended)¶
Webhooks allow Gencove to automatically notify your application when samples reach a final status. This is more efficient than polling and provides updates as soon as results are ready.
To set up webhooks see our integration guide.
Option 2: API Polling¶
If webhooks aren't feasible for your infrastructure, you can periodically poll the samples API to check for status updates.
For polling it's better to use the GET https://api.gencove.com/api/v2/project-samples/{project_id} endpoint that can be used for querying many samples at once. See our API reference for a detailed list of endpoints.
Downloading the deliverables¶
Both approaches above tell you when a sample is ready; they do not return the output files themselves. Once a sample reaches succeeded, fetch its deliverables as described in Downloading deliverables.
Gencove-Hosted Array Data Upload Form¶
The Request an Upload flow above has your application collect the end-user's file and upload it itself. As an alternative, Gencove can host the upload form for you: your backend requests a short-lived, tokenized URL and hands it to the end-user, who picks their own file on a Gencove-hosted page and submits it.
Use this when you'd rather not handle the file in your own application. Everything else in this guide applies unchanged—getting an API key, creating a project, tracking status, and receiving results.
How the Hosted Form Works¶
- Your application calls
POST /api/v2/array-data-upload-form/with an optionalproject_id(no file). - Gencove returns a
sample_idand a short-livedurl. - You hand the
urlto your end-user, who opens it and uploads their.zipfile onconsumer.gencove.com. - Gencove stores the file and schedules the sample, which then proceeds through the same statuses as the API path.
Create an Upload Session¶
When your end-user is ready to upload, your application should make a POST request to Gencove's array-data upload form endpoint.
API Endpoint: POST https://api.gencove.com/api/v2/array-data-upload-form/
Authentication: Include your API key in the Authorization header: Authorization: Api-Key $GENCOVE_API_KEY
Request Body:
{
"project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"metadata": {
"customer_id": "cust_12345",
"channel": "ios_webview"
},
"redirect_on_success": "https://partner.example.com/upload/done",
"redirect_on_failure": "https://partner.example.com/upload/error",
"expires_in": 600
}
Optional Fields:
| Field | Description |
|---|---|
project_id |
UUID of the project the sample should be created in; behaves exactly as described under Request an Upload |
metadata |
Custom key-value pairs stored on the sample (useful for linking back to your system) |
redirect_on_success |
HTTPS URL to send the end-user after a successful upload; omit to show a Gencove confirmation page |
redirect_on_failure |
HTTPS URL to send the end-user after a failed upload; Gencove appends a reason query parameter when possible |
expires_in |
How long the upload link stays valid before the end-user opens it, in seconds (default 600, minimum 60, maximum 3600) |
Response:
{
"sample_id": "6c3437b4-35fc-4224-9bb0-4623a16e6735",
"url": "https://consumer.gencove.com/array-data-upload?token=<jwt>",
"expires_at": "2026-06-02T12:34:56Z"
}
Important: Store the returned sample_id in your database. This is the unique identifier you'll use to track the sample's status and retrieve results. You can also find samples later using values in metadata.
Hand the returned url to your end-user when they are ready to upload (for example, open it in a mobile WebView). Create the session shortly before upload—the link expires after expires_in seconds.
curl -X POST "https://api.gencove.com/api/v2/array-data-upload-form/" \
-H "Authorization: Api-Key $GENCOVE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"metadata": {"customer_id": "cust_12345"},
"redirect_on_success": "https://partner.example.com/upload/done",
"redirect_on_failure": "https://partner.example.com/upload/error",
"expires_in": 600
}'
import os
import requests
API_KEY = os.getenv("GENCOVE_API_KEY")
headers = {
"Authorization": f"Api-Key {API_KEY}",
"Content-Type": "application/json",
}
payload = {
"project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"metadata": {"customer_id": "cust_12345"},
"redirect_on_success": "https://partner.example.com/upload/done",
"redirect_on_failure": "https://partner.example.com/upload/error",
"expires_in": 600,
}
response = requests.post(
"https://api.gencove.com/api/v2/array-data-upload-form/",
headers=headers,
json=payload,
)
response.raise_for_status()
session = response.json()
print(f"Sample ID: {session['sample_id']}")
print(f"Upload URL: {session['url']}")
End-User Upload Experience¶
After your end-user opens the url:
- Gencove validates the link and shows a branded upload form on
consumer.gencove.com - The end-user uploads their
.zipfile - On success, they are redirected to
redirect_on_successor see a Gencove confirmation page - On failure, they are redirected to
redirect_on_failurewhen configured
Once the file is uploaded, the sample is scheduled for analysis automatically. You do not need to make a separate API call to start processing.
Session timing: The upload link is valid for expires_in seconds before it is opened. After the end-user opens it, they have 15 minutes to complete the upload. If either window expires, create a new session.
One file per sample: Each upload session accepts a single .zip file. If a file was already uploaded for that sample, the link will no longer accept another upload.
Opening the Upload Form¶
Open the returned url as a top-level page—a new browser tab, an iOS ASWebAuthenticationSession, or an Android Custom Tab. Embedding the upload pages in an iframe is not supported.
Additional Sample Statuses¶
Hosted-form samples pass through two extra statuses before scheduled, in place of the API path's array data waiting for upload and array data upload expired (all other statuses match Tracking Sample Status above):
| Status | Meaning |
|---|---|
consumer awaiting data |
Session created; waiting for the end-user to upload |
consumer session expired |
Upload link expired before a file was received |
Manual Testing¶
You can start an upload session from the Gencove web UI:
- Open your array-data upload project
- Click New upload session on the project page
- Open the returned link and upload a test
.zipfile
This uses the same endpoint as the hosted-form integration described above.
System Responsibilities¶
Understanding what each system is responsible for helps clarify the integration:
Your Application's Responsibilities¶
- Handle end-user requests to upload array data
- Collect the
.zipexport from the end-user and validate it before sending - Request an upload via the Gencove API
- Store the Gencove sample ID for each upload
- Post the file to the returned URL, echoing
upload_fieldsback verbatim with the file part last - Request a new upload if the authorization expires or the upload fails
- Monitor sample status (via webhooks or polling)
- Retrieve and display results to your end-users
- Manage your end-users' accounts and permissions
- Keep the Gencove API Key secure
Gencove's Responsibilities¶
- Provide the API for requesting uploads and issuing upload authorizations
- Host the optional end-user upload experience on
consumer.gencove.com - Enforce the file type and size limits at upload time
- Store uploaded files and run array-data analysis
- Deliver results via webhooks or API
- Provide sample status tracking
Support¶
If you have questions about the Array Data Uploads integration or encounter any issues: