SUBSEQ.BIO
DOCS-API

API Reference

Base URL: https://subseq.bio/api/v1. Every response follows { "success": bool, "data"?: any, "error"?: string }. Send Authorization: Bearer <api_key> (or rely on the api_key cookie).

In every job container, uploaded or referenced data is mounted read-only at /inputs/, and auxiliary data is mounted read-only at /aux/. Results are written to /outputs/.

Rate limits

Per client IP: token bucket with burst of 10 requests, averaging ~6 req/s. Global: 800 requests per 10 seconds across all clients. If you receive HTTP 429, honor the Retry-After header (seconds to wait) or back off with exponential jitter.

Jobs

POST /job/submit

Launch a job via multipart/form-data. Include program, repeatable args, and one input source: uploaded folder files, input_src=dataset with digest, or input_src=job with from_job. input_src=upload is accepted but may be omitted when sending folder files. Optional fields include project, memo, deadline_minutes, aux_src (none | upload | dataset | job), aux_ref, and auxiliary file uploads via aux form fields (uploaded into /aux; aux_src=upload may be omitted when sending aux files).

Upload limits: request body up to 64 GiB total, and up to 20,000 files per upload field (folder and aux each).

curl -X POST https://subseq.bio/api/v1/job/submit \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -F program=alphafold2 \
    -F input_src=upload \
    -F memo="Benchmark run 12" \
    -F args=--fasta_paths=data/sample.fasta \
    -F "folder=@data/sample.fasta;filename=data/sample.fasta"
{
  "success": true,
  "data": {
    "job_id": "5bf2d83e40a47cf5"
  }
}

POST /job/submit-guided

Launch a job through a program-specific guided schema. Use GET /program/params?program=... first to discover whether a program supports guided submit and which typed params it accepts. The request is multipart/form-data with program and params (a JSON object); optional project, memo, and the same uploaded folder, dataset digest, and previous-job from_job input fields from /job/submit are supported when guided file params refer to files under /inputs. input_src=upload may be omitted when sending folder files, but uploads cannot be combined with dataset or previous-job inputs. Guided auxiliary file params use the same aux_src/aux_ref/aux fields as /job/submit.

Do not send raw args or deadline_minutes; the guided builder owns server args, generated configs, /outputs, and related server paths. Guided input file and folder params accept source-relative paths such as target.pdb, /target.pdb, inputs/target.pdb, or /inputs/target.pdb. Guided auxiliary file params accept source-relative auxiliary paths such as main.py or aux/main.py.

curl -X POST https://subseq.bio/api/v1/job/submit-guided \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -F program=rfdiffusion3 \
    -F memo="guided RFD3 monomer" \
    -F 'params={"mode":"unconditional_monomer","length":"120-130","num_structures":10}'
{
  "success": true,
  "data": {
    "job_id": "5bf2d83e40a47cf5"
  }
}

GET /job/list

List jobs (query params: limit <=100, offset, status, project, program, pipeline_id, before=YYYY-MM-DD). Each row includes input_src/input_ref and aux_src/aux_ref when the job depended on prior data, plus memo if one was provided.

curl -G https://subseq.bio/api/v1/job/list \
    -H "Authorization: Bearer sk-ss-admin-123" \
    --data-urlencode "limit=5" \
    --data-urlencode "status=completed" \
    --data-urlencode "program=alphafold2"
{
  "success": true,
  "data": {
    "total": 12,
    "jobs": [
      {
        "job_id": "5bf2d83e40a47cf5",
        "status": "completed",
        "cost": 18.5,
        "program": "alphafold2",
        "input_src": "dataset",
        "input_ref": "08f9f54a1d55e548e54a9b857f2df70f44c2bcfe6ff5b846c8d3107568e1b6d9",
        "project": "default",
        "memo": "Benchmark run 12",
        "aux_src": "job",
        "aux_ref": "5bf2d83e40a47cf4",
        "created_at": 1730678400,
        "started_at": 1730678465,
        "finished_at": 1730680200,
        "last_charged_at": 1730679600
      }
    ]
  }
}

GET /job/view

Inspect a single job; requires jobid query. Returns input_src/input_ref and aux_src/aux_ref when the job used a dataset or another job as its source (omitted for generated jobs). Includes memo if provided and a manifest for finished jobs.

curl "https://subseq.bio/api/v1/job/view?jobid=5bf2d83e40a47cf5" \
    -H "Authorization: Bearer sk-ss-admin-123"
{
  "success": true,
  "data": {
    "status": "completed",
    "cost": 18.5,
    "program": "alphafold2",
    "user_args": "--fasta_paths=data/sample.fasta",
    "server_args": "--max_template_date=2023-10-01",
    "project": "default",
    "memo": "Benchmark run 12",
    "input_src": "job",
    "input_ref": "5bf2d83e40a47cf4",
    "aux_src": "dataset",
    "aux_ref": "08f9f54a1d55e548e54a9b857f2df70f44c2bcfe6ff5b846c8d3107568e1b6d9",
    "created_at": 1730678400,
    "started_at": 1730678465,
    "finished_at": 1730680200,
    "log_tail": "Job finished successfully.",
    "manifest": [
      {
        "path": "outputs/prediction.pdb",
        "sha256": "b3b0bf9cf3b5c7d98a7d4012ee6324de470b01fb6bc2cbbb0f1e46f9d3db15bc",
        "size": 2456789
      }
    ]
  }
}

POST /job/rerun

Create a new standalone job from a completed, failed, or canceled job. The rerun reuses the same program, validated arguments, project, deadline, and input/aux sources; uploaded input or auxiliary files are copied server-side. The new memo is set to Rerun of <jobid>: <prev memo>.

curl -X POST https://subseq.bio/api/v1/job/rerun \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{"job_id":"5bf2d83e40a47cf5"}'
{
  "success": true,
  "data": {
    "job_id": "6cf3e94f51b58d06",
    "rerun_of_job_id": "5bf2d83e40a47cf5"
  }
}

POST /job/cancel

Cancel a pending or running job via ?jobid=.

curl -X POST "https://subseq.bio/api/v1/job/cancel?jobid=5bf2d83e40a47cf5" \
    -H "Authorization: Bearer sk-ss-admin-123"
{
  "success": true,
  "data": "Job cancelled successfully"
}

GET /job/download

Download the job archive ZIP (?jobid=, optional accept_partial_data=true for failed/canceled jobs).

curl -L -o job_5bf2d83e40a47cf5.zip \
    "https://subseq.bio/api/v1/job/download?jobid=5bf2d83e40a47cf5" \
    -H "Authorization: Bearer sk-ss-admin-123"
HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment; filename="job_5bf2d83e40a47cf5.zip"

GET /job/file

Stream a single output (?jobid= & path= from manifest). Supports GET/HEAD.

curl -H "Authorization: Bearer sk-ss-admin-123" \
    "https://subseq.bio/api/v1/job/file?jobid=5bf2d83e40a47cf5&path=outputs/prediction.pdb" \
  | head
HTTP/1.1 200 OK
Content-Type: chemical/x-pdb
X-Subseq-File: 1
...raw file bytes...

POST /job/move-project

Reassign a job to an existing project (empty project removes assignment).

curl -X POST https://subseq.bio/api/v1/job/move-project \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{"job_id":"5bf2d83e40a47cf5","project":"archive"}'
{
  "success": true
}

Pipelines

POST /pipeline/submit

Create an auto-managed pipeline. Pipelines are ordered job steps that execute back-to-back. Each pipeline has one top-level pipeline input for shared static files, scripts, configs, targets, ligands, and other campaign assets. Step 2+ automatically consume the previous step outputs as their main input.

Each step can use either raw args or guided params. Do not include per-step input_src, input_ref, aux_src, or aux_ref; use top-level pipeline_input_src and pipeline_input_ref. Valid pipeline input sources are none, upload, and dataset. Multipart submits can upload files with the pipeline_input field when pipeline_input_src=upload.

curl -X POST https://subseq.bio/api/v1/pipeline/submit \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{
  "project": "default",
  "memo": "Two-step chain",
  "pipeline_input_src": "dataset",
  "pipeline_input_ref": "08f9f54a1d55e548e54a9b857f2df70f44c2bcfe6ff5b846c8d3107568e1b6d9",
  "steps": [
    {
      "program": "rfdiffusion3",
      "params": {
        "mode": "unconditional_monomer",
        "length": "80-90",
        "num_structures": 1
      }
    },
    {
      "program": "ligandmpnn",
      "args": ["--model_type=protein_mpnn"]
    }
  ]
}'
{
  "success": true,
  "data": {
    "pipeline_id": "ab12cd34",
    "total_steps": 2
  }
}

GET /pipeline/list

List pipelines (query params: limit <=100, offset, status, project).

curl -G https://subseq.bio/api/v1/pipeline/list \
    -H "Authorization: Bearer sk-ss-admin-123" \
    --data-urlencode "limit=5"
{
  "success": true,
  "data": {
    "total": 1,
    "pipelines": [
      {
        "pipeline_id": "ab12cd34",
        "status": "running",
        "project": "default",
        "memo": "Two-step chain",
        "active_job_id": "5bf2d83e40a47cf5",
        "active_program": "rfdiffusion",
        "next_step": 2,
        "total_steps": 2,
        "finished_at": 0
      }
    ]
  }
}

GET /pipeline/info

Inspect a pipeline by ?pipeline_id=. Includes steps_json (stringified JSON array of normalized steps), status, active job, and progress counters.

curl "https://subseq.bio/api/v1/pipeline/info?pipeline_id=ab12cd34" \
    -H "Authorization: Bearer sk-ss-admin-123"
{
  "success": true,
  "data": {
    "pipeline_id": "ab12cd34",
    "project": "default",
    "memo": "Two-step chain",
    "status": "running",
    "steps_json": "[{\"program\":\"rfdiffusion3\",\"params\":{\"mode\":\"unconditional_monomer\",\"length\":\"80-90\",\"num_structures\":1}}]",
    "active_job_id": "5bf2d83e40a47cf5",
    "next_step": 2,
    "total_steps": 2,
    "finished_at": 0
  }
}

Datasets

POST /dataset/new

Create a dataset: provide project, name, and either upload files via folder parts or set from_job to copy job outputs.

Use a short human label for name. Do not include dates or timestamps; creation time is already tracked in created_at.

Upload limits: request body up to 64 GiB total, and up to 20,000 files in folder.

Folder uploads strip the selected top-level folder. For example, run1/inputs/a.fa is stored as inputs/a.fa.

curl -X POST https://subseq.bio/api/v1/dataset/new \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -F project=default \
    -F name=protein-models \
    -F "folder=@outputs/prediction.pdb;filename=prediction.pdb"
{
  "success": true,
  "data": {
    "digest": "a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a"
  }
}

POST /dataset/upload/presign

Mint an expiring, unauthenticated upload URL for a dataset ZIP archive. Requires SUBSEQ_PRESIGN_HMAC_SECRET to be set.

ZIP uploads strip one common top-level wrapper directory when all files share it; otherwise archive-relative paths are preserved. For example, bundle/prediction_inputs/a.fa becomes prediction_inputs/a.fa, while sibling directories such as boltz/a.yaml and msas/target.a3m stay nested.

curl -X POST https://subseq.bio/api/v1/dataset/upload/presign \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{
  "project": "default",
  "name": "protein-models",
  "size_bytes": 123456789,
  "format": "zip",
  "sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
  "ttl_seconds": 3600
}'
{
  "success": true,
  "data": {
    "upload_url": "https://subseq.bio/api/v1/dataset/upload?token=...",
    "token": "...",
    "expires_at": 1730683800,
    "method": "PUT",
    "max_bytes": 123456789
  }
}

PUT /dataset/upload

Unauthenticated upload endpoint. Provide the presigned token as a query parameter. Upload a ZIP archive to create the dataset.

curl -X PUT -T protein-models.zip \
    "https://subseq.bio/api/v1/dataset/upload?token=..."
{
  "success": true,
  "data": {
    "digest": "a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a"
  }
}

POST /dataset/edit

Edit a ready dataset by applying file deletes/additions. Use mode=update to keep the same dataset name, or mode=save_as to create under a new name.

curl -X POST https://subseq.bio/api/v1/dataset/edit \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -F "base_digest=a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a" \
    -F "mode=update" \
    -F "delete=obsolete/model_old.pdb" \
    -F "folder=@prediction_v2.pdb;filename=prediction_v2.pdb"
{
  "success": true,
  "data": {
    "base_digest": "a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a",
    "digest": "b2d7ad0fc2d88e35de9040f3fd3489e00cfba28d8803cbdc29a20d7db7787ba2",
    "name": "protein-models",
    "project": "default",
    "mode": "update",
    "changed": true
  }
}

POST /dataset/delete

Delete by ?digest= or ?name=.

curl -X POST "https://subseq.bio/api/v1/dataset/delete?digest=a3d0...c8a" \
    -H "Authorization: Bearer sk-ss-admin-123"
{
  "success": true
}

GET /dataset/info

Inspect dataset metadata (?digest= or ?name=).

curl "https://subseq.bio/api/v1/dataset/info?digest=a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a" \
    -H "Authorization: Bearer sk-ss-admin-123"
{
  "success": true,
  "data": {
    "files": [
      {
        "path": "prediction.pdb",
        "sha256": "936a1ec533d4c0bb7e90bcab44cbb6f4c012cfc16f0bcdf5ddf5d24c9fd4da45",
        "size": 2456789
      }
    ],
    "meta": {
      "name": "protein-models",
      "digest": "a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a",
      "project": "default",
      "job_id": "5bf2d83e40a47cf5",
      "size": 2456789,
      "ready": true,
      "dl_ready": true,
      "created_at": "2025-10-01T17:20:45Z"
    }
  }
}

GET /dataset/list

List datasets (query: limit, offset, project). Returns the latest version per dataset name lineage.

curl -G https://subseq.bio/api/v1/dataset/list \
    -H "Authorization: Bearer sk-ss-admin-123" \
    --data-urlencode "limit=10"
{
  "success": true,
  "data": {
    "total": 4,
    "datasets": [
      {
        "digest": "a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a",
        "name": "protein-models",
        "project": "default",
        "job_id": "5bf2d83e40a47cf5",
        "size": 2456789,
        "ready": true,
        "dl_ready": true,
        "created_at": 1730410800
      }
    ]
  }
}

GET /dataset/history

List version history for a dataset name. Query: name (required), limit, offset.

curl -G https://subseq.bio/api/v1/dataset/history \
    -H "Authorization: Bearer sk-ss-admin-123" \
    --data-urlencode "name=protein-models" \
    --data-urlencode "limit=25"
{
  "success": true,
  "data": {
    "total": 3,
    "datasets": [
      {
        "digest": "b2d7ad0fc2d88e35de9040f3fd3489e00cfba28d8803cbdc29a20d7db7787ba2",
        "name": "protein-models",
        "project": "default",
        "job_id": "",
        "size": 2478910,
        "ready": true,
        "dl_ready": true,
        "created_at": 1730497200
      },
      {
        "digest": "a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a",
        "name": "protein-models",
        "project": "default",
        "job_id": "5bf2d83e40a47cf5",
        "size": 2456789,
        "ready": true,
        "dl_ready": true,
        "created_at": 1730410800
      }
    ]
  }
}

GET /dataset/download

Download the dataset archive ZIP (available when dl_ready is true).

curl -L -o dataset_a3d0f65b.zip \
    "https://subseq.bio/api/v1/dataset/download?digest=a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a" \
    -H "Authorization: Bearer sk-ss-admin-123"
HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment; filename="dataset_a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a.zip"

GET /dataset/file

Stream a file from a ready dataset (?digest= & path=).

curl -H "Authorization: Bearer sk-ss-admin-123" \
    "https://subseq.bio/api/v1/dataset/file?digest=a3d0...c8a&path=prediction.pdb" \
  | head
HTTP/1.1 200 OK
Content-Type: chemical/x-pdb
X-Subseq-File: 1
...raw file bytes...

POST /dataset/move-project

Move a dataset lineage to an existing project (or clear by sending an empty string). All snapshots with the same name are updated.

curl -X POST https://subseq.bio/api/v1/dataset/move-project \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{"digest":"a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a","project":"archive"}'
{
  "success": true
}

POST /dataset/rename

Rename a dataset lineage. All snapshots with the same name are renamed together.

curl -X POST https://subseq.bio/api/v1/dataset/rename \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{"name":"protein-models","new_name":"protein-models-v2"}'
{
  "success": true
}

Presigned downloads

Mint short-lived, unauthenticated download links using a stateless HMAC-signed token. The minted link can be opened in a browser or shared with someone who should be able to download the data. The token expires after ttl_seconds (default: 3600; max: 604800).

Server config: requires SUBSEQ_PRESIGN_HMAC_SECRET to be set. All presigned downloads require dl_ready=true in the DB.

POST /download/presign

Authenticated endpoint to mint a presigned URL.

type is one of: job_download, job_file, dataset_download, dataset_file.

curl -X POST https://subseq.bio/api/v1/download/presign \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{
  "type": "job_download",
  "jobid": "5bf2d83e40a47cf5",
  "ttl_seconds": 3600
}'
{
  "success": true,
  "data": {
    "url": "https://subseq.bio/d?token=...",
    "token": "...",
    "expires_at": 1730683800
  }
}

For failed/canceled jobs, include "accept_partial_data": true when minting job_download.

Mint a presigned URL for a single job file:

curl -X POST https://subseq.bio/api/v1/download/presign \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{
  "type": "job_file",
  "jobid": "5bf2d83e40a47cf5",
  "path": "outputs/prediction.pdb",
  "ttl_seconds": 600
}'

Mint a presigned URL for a dataset archive:

curl -X POST https://subseq.bio/api/v1/download/presign \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{
  "type": "dataset_download",
  "digest": "a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a",
  "ttl_seconds": 3600
}'

Mint a presigned URL for a single dataset file:

curl -X POST https://subseq.bio/api/v1/download/presign \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{
  "type": "dataset_file",
  "digest": "a3d0f65ba9e14fdb8a95d9f31f4bc7b7ca5f2d93e8f12c6d42c439ab20c77c8a",
  "path": "prediction.pdb",
  "ttl_seconds": 600
}'

GET /download

Unauthenticated download endpoint. Provide the presigned token as a query parameter. Returns either a ZIP archive or a single file, depending on the token type.

curl -L -o out.zip "https://subseq.bio/d?token=..."
HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment; filename="job_5bf2d83e40a47cf5.zip"

Projects

POST /project/new

Create (or idempotently ensure) a project name.

curl -X POST https://subseq.bio/api/v1/project/new \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{"name":"archive"}'
{
  "success": true
}

GET /project/list

Return all projects for the account.

curl https://subseq.bio/api/v1/project/list \
    -H "Authorization: Bearer sk-ss-admin-123"
{
  "success": true,
  "data": [
    { "name": "archive", "created_at": 1730238000 },
    { "name": "default", "created_at": 1728000000 }
  ]
}

POST /project/delete

Delete a project and clear it from jobs/datasets.

curl -X POST https://subseq.bio/api/v1/project/delete \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{"name":"archive"}'
{
  "success": true
}

Public

GET /status

Service heartbeat.

curl https://subseq.bio/api/v1/status
{
  "success": true
}

GET /program/list

List active programs with pricing info.

curl https://subseq.bio/api/v1/program/list
{
  "success": true,
  "data": [
    {
      "name": "alphafold2",
      "display_name": "AlphaFold2",
      "description": "AlphaFold2 monomer inference with reduced DBs.",
      "cost_per_hour": 14.5,
      "submit_fee": 0,
      "min_run_time": 900
    }
  ]
}

GET /program/params

Return the typed guided submission schema for one active program (?program=). A guided program returns guided: true plus ordered modes and params; a program without guided support returns guided: false with a message.

Each param includes a stable name, type, and label, with optional metadata such as required, default, options, extensions, min, max, bounds, depends_on, modes, selection, and help. Send selected values back as the JSON params field to /job/submit-guided.

curl "https://subseq.bio/api/v1/program/params?program=rfdiffusion3"

Abridged response:

{
  "success": true,
  "data": {
    "program": "rfdiffusion3",
    "guided": true,
    "display_name": "RFdiffusion3",
    "description": "RFdiffusion3 design for binders, unconditional monomers, and symmetric oligomers.",
    "modes": [
      {
        "name": "binder_design",
        "label": "Binder Design",
        "description": "Design binders against a target structure using contigs and hotspot residues.",
        "default": true
      },
      {
        "name": "unconditional_monomer",
        "label": "Unconditional Monomer",
        "description": "Generate de novo monomers from a requested length range."
      }
    ],
    "params": [
      {
        "name": "target_structure",
        "type": "structure_file",
        "label": "Target Structure",
        "required": true,
        "modes": ["binder_design"],
        "extensions": [".pdb", ".cif"]
      },
      {
        "name": "length",
        "type": "length_range",
        "label": "Length",
        "required": true,
        "placeholder": "120-130",
        "modes": ["unconditional_monomer"],
        "min": 3,
        "max": 2000
      },
      {
        "name": "num_structures",
        "type": "int",
        "label": "Structures",
        "default": 10,
        "min": 1,
        "max": 10000
      },
      {
        "name": "output_format",
        "type": "string",
        "label": "Output Type",
        "default": "cif",
        "options": ["cif", "pdb", "cif.gz"]
      }
    ]
  }
}

GET /pricing

Current submit fee and server-configured payment top-up maximum.

curl https://subseq.bio/api/v1/pricing
{
  "success": true,
  "data": {
    "submit_fee": 0,
    "top_up_max_credits": 5000
  }
}

Use data.top_up_max_credits as the numeric maximum for payment credit requests.

Auth & Account

POST /auth/otp

Send a one-time code to email ({ "email": string }).

curl -X POST https://subseq.bio/api/v1/auth/otp \
    -H "Content-Type: application/json" \
    -d '{"email":"you@example.com"}'
{
  "success": true
}

POST /auth/login

Exchange OTP for an admin API key. type=api returns the key; web sets the cookie.

curl -X POST https://subseq.bio/api/v1/auth/login \
    -H "Content-Type: application/json" \
    -d '{"email":"you@example.com","otp":"123456","type":"api"}'
{
  "success": true,
  "data": {
    "token": "sk-ss-admin-123",
    "expires_at": 1733107200
  }
}

GET /account/info

Retrieve credits and metadata.

curl https://subseq.bio/api/v1/account/info \
    -H "Authorization: Bearer sk-ss-admin-123"
{
  "success": true,
  "data": {
    "email": "you@example.com",
    "credits": 87.5,
    "created_at": 1693526400,
    "last_activity": 1733073600
  }
}

POST /account/logout

Invalidate the current admin key (Authorization header or cookie).

curl -X POST https://subseq.bio/api/v1/account/logout \
    -H "Authorization: Bearer sk-ss-admin-123"
{
  "success": true
}

API Keys

Requires an admin key from /auth/login. Returned keys are non-admin client keys.

GET /key/list

List non-admin keys.

curl https://subseq.bio/api/v1/key/list \
    -H "Authorization: Bearer sk-ss-admin-123"
{
  "success": true,
  "data": [
    {
      "key_hash": "api_9ad2f7...",
      "hint": "sk-ss-api-13****",
      "label": "cli",
      "expires_at": 1734316800
    }
  ]
}

POST /key/new

Create a non-admin key (optional label, expires_in_days).

curl -X POST https://subseq.bio/api/v1/key/new \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{"label":"cli","expires_in_days":30}'
{
  "success": true,
  "data": {
    "key": "sk-ss-api-13abcdefg"
  }
}

POST /key/delete

Delete a non-admin key by key_hash.

curl -X POST https://subseq.bio/api/v1/key/delete \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{"key_hash":"api_9ad2f7..."}'
{
  "success": true
}

Payments

POST /payment/create-checkout

Create a Stripe Checkout session ({ "credits": int }, minimum 1 and maximum top_up_max_credits from /pricing).

curl -X POST https://subseq.bio/api/v1/payment/create-checkout \
    -H "Authorization: Bearer sk-ss-admin-123" \
    -H "Content-Type: application/json" \
    -d '{"credits":50}'
{
  "success": true,
  "data": {
    "url": "https://checkout.stripe.com/c/pay/cs_test_a1b2..."
  }
}

GET /payment/verify

Finalize a Stripe payment (?session_id=, ?tx= from checkout).

curl "https://subseq.bio/api/v1/payment/verify?session_id=cs_test_a1b2&tx=tx-6f828251" \
    -H "Authorization: Bearer sk-ss-admin-123"
{
  "success": true,
  "data": {
    "url": "https://subseq.bio"
  }
}

POST /payment/x402/topup

Add credits with x402 using USDC on Base ({ "credits": int }, minimum 1 and maximum top_up_max_credits from /pricing). This endpoint is intended for AI agents and other automated clients.

curl -i -X POST https://subseq.bio/api/v1/payment/x402/topup \
    -H "Authorization: Bearer sk-ss-api-123" \
    -H "Content-Type: application/json" \
    -d '{"credits":1}'

If no payment is attached, the server returns 402 Payment Required with a PAYMENT-REQUIRED header describing the required USDC payment.

HTTP/2 402
PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQQVlNRU5ULVNJR05BVFVSRS...
WWW-Authenticate: x402
{
  "x402Version": 2,
  "error": "PAYMENT-SIGNATURE header is required",
  "resource": {
    "url": "https://subseq.bio/api/v1/payment/x402/topup",
    "description": "Add compute credits to a subseq.bio account",
    "mimeType": "application/json",
    "serviceName": "subseq.bio",
    "tags": ["bioinformatics", "compute", "credits"]
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "1000000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x...",
      "maxTimeoutSeconds": 60,
      "extra": {
        "name": "USD Coin",
        "version": "2"
      }
    }
  ]
}

An x402-aware client retries the same request with the PAYMENT-SIGNATURE header. After successful settlement, the account balance is credited immediately.

{
  "success": true,
  "data": {
    "txid": "tx-6f828251",
    "credits": 1,
    "currency": "usdc",
    "provider_ref": "0x...",
    "payer": "0x...",
    "network": "eip155:8453",
    "amount": "1000000"
  }
}

GET /tx/list

List transactions (query: limit, offset, status=pending|success|failed|any).

curl -G https://subseq.bio/api/v1/tx/list \
    -H "Authorization: Bearer sk-ss-admin-123" \
    --data-urlencode "limit=5"
{
  "success": true,
  "data": {
    "total": 3,
    "txs": [
      {
        "txid": "tx-6f828251",
        "user_id": 42,
        "email": "you@example.com",
        "amount": 50,
        "currency": "usd",
        "status": "success",
        "created_at": 1733071800,
        "completed_at": 1733071860
      }
    ]
  }
}

Error Model

Failures use HTTP 4xx/5xx with {"success": false, "error": "message"}. Common cases: 400 invalid input, 401 missing/expired auth, 403 insufficient credits or non-admin, 404 missing resource, 429 rate limit, 503 compute backend offline.