Skip to content

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_id in 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:

  1. Your end-user: The person supplying their genotyping export
  2. Your application (consumer health company): Collects the file, integrates with Gencove's API, and uploads the file
  3. 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

  1. File collected: Your end-user supplies their 23andMe, Ancestry, or MyHeritage .zip export in your application
  2. Accept request: Your application accepts the file and stores necessary information
  3. Request an upload: Your application makes an authenticated request to Gencove's API to request an upload
  4. Sample ID and authorization: Gencove returns a unique sample ID, an upload URL, and the form fields that authorize the upload
  5. Upload the file: Your application posts the .zip file to that URL, echoing back the returned fields with the file part last
  6. Gencove analysis: Gencove stores the file, schedules the sample, and runs analysis on the array data
  7. Results delivery via webhook: (RECOMMENDED) Gencove delivers results to your application via webhook when they're ready
  8. Fetch results via API: Periodically ping the Gencove API to get sample status and results
  9. 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:

  1. Log in to your Gencove account at web.gencove.com
  2. In the left sidebar, click on Account
  3. Navigate to the API Keys tab
  4. Click the Generate new API key button
  5. Copy the generated API key and store it securely

Security Best Practice: For production use, consider creating a separate Gencove user with Analyst level 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_fields back 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:

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

  1. Your application calls POST /api/v2/array-data-upload-form/ with an optional project_id (no file).
  2. Gencove returns a sample_id and a short-lived url.
  3. You hand the url to your end-user, who opens it and uploads their .zip file on consumer.gencove.com.
  4. 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:

  1. Gencove validates the link and shows a branded upload form on consumer.gencove.com
  2. The end-user uploads their .zip file
  3. On success, they are redirected to redirect_on_success or see a Gencove confirmation page
  4. On failure, they are redirected to redirect_on_failure when 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:

  1. Open your array-data upload project
  2. Click New upload session on the project page
  3. Open the returned link and upload a test .zip file

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 .zip export 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_fields back 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: