# Data Access clients

Use these Node.js clients to retrieve and verify VeriProof publications. Start with the [reference receiver](REFERENCE-RECEIVER.md) if you want to test a complete import into SQLite. For file retrieval alone, use the pull or polling commands below.

The customer portal's **Setup and retrieval guide** links to the complete download. Extract the scripts into one folder and run the commands there.

## Configure authentication

Open **Settings → Integrations → Data Access** and register an integration. Choose its applications, datasets, and frameworks, then select an authentication method.

| Method | Environment variables |
| --- | --- |
| Signing key | `VERIPROOF_CLIENT_ID`, `VERIPROOF_SIGNING_KEY_ID`, `VERIPROOF_ASSERTION_AUDIENCE`, `VERIPROOF_PRIVATE_KEY_PATH` |
| Client secret | `VERIPROOF_CLIENT_ID`, `VERIPROOF_CLIENT_SECRET` |

For a signing key, register the P-256 public key and keep the private key in your receiving system. Leave `VERIPROOF_CLIENT_SECRET` unset. The client creates a fresh ES256 assertion for each token request. Copy the exact audience from the portal, even when you're using a local proxy.

For a client secret, save the one-time value shown during registration in your secret store. Supply it through the process environment. Keep credentials out of command arguments, source files, and logs.

You'll also need the publisher's public verification key from a separately trusted channel. Don't accept a key supplied with an untrusted bundle: someone could replace both the files and the key.

## Pull a publication

Use `GET /v1/data-access/publications` to find a publication ID, then run:

```text
node pull-publication.mjs <API-origin> <publication-id> <new-directory> <trusted-public-key.pem>
```

The parent directory must exist, and the publication directory must be new. The client requests a token, downloads the JSON, PDF, and manifest, verifies them, and checks availability again. It requires HTTPS outside loopback development and refuses redirects.

If the pull fails, leave any partial files out of your import process. A successful run gives you verified files; use the reference receiver or your own importer to save them into a business application.

The [OpenAPI contract](openapi.json) describes the token and read endpoints, publication envelopes, manifests, and dataset schemas. Preserve original JSON when mapping fields. Missing values differ from zero, and later responses may include additional fields.

## Verify downloaded files

Place `manifest.json`, `data.json`, and `report.pdf` in one directory. The verifier uses built-in Node.js modules and needs no package installation.

```text
node verify-publication.mjs <publication-directory> <trusted-public-key.pem> <publication-id>
```

Use the publication ID your integration requested. Exit code `0` means verification passed, `1` means it failed, and `2` means required arguments are missing. Import only files that pass verification.

When implementing your own verifier:

1. Decode the manifest's `payloadBase64` and verify those exact bytes with ECDSA P-256 and SHA-256. Use the DER-encoded `signature`; don't parse and reserialize the payload first.
2. Check the publication ID, application scope, file lengths, and SHA-256 hashes against the signed values.
3. Check that the manifest and JSON agree on their publication details and any correction reference.

The public-key fingerprint is the lowercase SHA-256 hash of the DER SubjectPublicKeyInfo.

JSON includes the schema version, publication and dataset IDs, application scope, source identity, timestamps, content-policy version, and data. Framework assessments also preserve source evaluation IDs and each control's reporting period. Oversight decisions, human reviews, and execution acknowledgments remain separate records.

Verification confirms that the files match what the trusted publisher signed. Recheck availability before a later import, and apply your application's validation and review process to the records.

## Poll with a durable retrieval checkpoint

Keep `poll-publications.mjs`, `pull-publication.mjs`, and `verify-publication.mjs` together. Use the same credentials and trusted key as the pull client.

```text
node poll-publications.mjs <API-origin> <polling-directory> <trusted-public-key.pem>
```

Each run processes up to 100 records. Run it again while `More pages` is true, then schedule regular checks. A verified availability notification can trigger the same command.

The directory belongs to one API origin and client ID. Verified folders are named with their publication IDs, and adjacent status files record the last observed availability.

Progress is saved after local processing, including when a page is empty. If a run stops partway through a page, the next run resumes from its saved results. Downloads pass through `.pending` folders before verification and the move to their final location. Leave unfinished folders out of manual imports.

Run one process per polling directory. After a crash, check that the process recorded in `poll.lock` has stopped before removing that file. For an access change or expired cursor, start a new polling directory and reconcile destination records by publication ID.

Before importing older downloads, check current availability and enforce the signed expiry. Expiry alone doesn't create a new listing position. Local files remain after expiry or withdrawal, so your importer must use the latest availability information.

To add an import step, pass a `processPublication` callback to `pollPublicationPage`. Return only after the destination commits the records and an import receipt. If the process stops before the checkpoint saves, the callback can run again. Reuse the existing receipt to avoid duplicate records, and retain those receipts when a changed listing page is retried.

## Map records into your application

Begin with a saved framework assessment and its control results. Keep these fields with the receiving record:

| Source | Use in your application |
| --- | --- |
| API origin, integration ID, `publicationId` | Identify the publication and recognize retries |
| `datasetId`, `applicationIds`, `frameworkCode` | Preserve the application and framework scope |
| `sourceIdentity` and evaluation/control IDs | Trace results to the saved assessment revision |
| `generatedAt` and coverage dates | Separate file generation from the reporting period |
| Signed manifest `expiresAt` | Enforce the retrieval lifetime |
| `schemaVersion`, `contentPolicyVersion` | Choose the parser and retain the content-policy context |
| Signed manifest and file hashes | Keep verification evidence with the original files |
| Import time and destination record ID | Record the completed destination import |

Save each publication as a separate source revision. Matching application and framework IDs don't make two assessment revisions interchangeable. Preserve missing values as missing, and keep the original JSON alongside mapped fields.

Commit the publication, controls, file references, and receipt together. On retry, return the existing receipt instead of adding duplicate records.

For a withdrawal, retain the audit history and exclude the source from evidence used for new decisions. If it supported an earlier decision, route it through your existing review process. Apply your retention policy to imported copies.

### Check the receiving workflow

1. Import an authorized assessment and compare its control count, results, evaluation IDs, periods, and file hashes with the publication.
2. Import it again, including after an interruption between the destination commit and checkpoint save. Confirm that only one source revision exists.
3. Change a file or supply an untrusted signature. Confirm rejection without a partial import or checkpoint advance.
4. Import a later revision for the same application and framework. Keep both revisions and check which one your evidence-selection rule chooses.
5. Process a withdrawal and an expired publication. Block new imports of unavailable files, and apply your evidence policy to assessments already received.
6. Revoke access and confirm that reads fail. Also check that records outside the integration's application scope stay excluded.

Record the destination IDs, import receipts, comparison results, and audit events so you can review the outcome.

## Repository contract qualification

Maintainers can check previously verified publication folders against the OpenAPI payload schemas:

```text
node integrations/data-access/validate-local-contract.mjs <publication-cache-directory>
```

This repository helper uses the portal's development validator and isn't included in the standalone download. It checks payload structure and selected rejection cases. Run signature verification separately.

## Correction relationships

A correction has its own publication ID and a `correctsPublicationId` reference in the API summary, JSON, and signed manifest. Both publications share the integration, dataset, framework, and application scope.

Keep both IDs and the relationship, even if the earlier version expires or is withdrawn. The earlier publication remains available until withdrawal or expiry. Your review workflow decides how to use the correction. The verification utility checks that the manifest and JSON agree on the reference.
