Structured Ingots API v1

Overview

Structured Ingots provide encrypted key/value storage within your Vaults. Unlike standard ingots (files), structured ingots store named fields that can be individually retrieved without downloading the entire ingot.

10K
Keys per Ingot
1MB
Max Value Size
AES-256
Encryption
Atomic
Set/Delete Ops

Use Cases

  • PII Storage: SSN, phone numbers, addresses per customer
  • Credentials: API keys, passwords, secrets with field-level access
  • User Preferences: Encrypted user settings with individual key access
  • Tokenized Data: Store sensitive data, retrieve as ephemeral Sparks
Values Returned as Sparks

For security, values are never returned inline. When you request keys, you receive Spark IDs that can be read once via the Sparks API.

Authentication

All Structured Ingots endpoints require account authentication (a JWT Bearer token or an API key) plus a Vault Access Token (VAT) obtained by unsealing the vault.

Headers

ParameterTypeRequiredDescription
Authorization string Optional JWT Bearer token (Authorization: Bearer <token>). Provide this or X-API-Key.
X-API-Key string Optional Your SparkVault API key. Provide this or Authorization.
X-Vault-Access-Token string Required VAT from unsealing the vault. Missing header → 400 VALIDATION_ERROR; unknown or malformed VAT → 401 AUTHENTICATION_ERROR; expired VAT → 403 FORBIDDEN.

API Endpoints

All JSON responses use the {"data": ...} envelope. Successful requests return 201 Created for POST, 200 OK for GET and PATCH, and 204 No Content for DELETE.

Create Structured Ingot

POST /v1/products/structured-ingots/{vault_id}

Create a new structured ingot with initial key/value data.

Request Body

ParameterTypeRequiredDescription
name string Required Name for the ingot (max 255 characters). A create with a name that already exists in the vault overwrites the existing structured ingot in place: it resets version to 1 and replaces every stored key, so the prior contents and keys are destroyed. Two structured ingots in one vault cannot share a name; use a fresh name (or PATCH the existing ingot) unless you intend to replace it.
data object Required Key/value pairs to store (max 10,000 keys)

Response

FieldTypeDescription
ingot_id string Unique ingot identifier (ing_...)
name string Ingot name
version integer Version number (starts at 1)
key_count integer Number of keys stored
created_at integer Creation timestamp

Example

Request
bash
curl -X POST https://api.sparkvault.com/v1/products/structured-ingots/vlt_abc123 \
  -H "X-API-Key: sv_live_xxx" \
  -H "X-Vault-Access-Token: YOUR_VAT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "customer_12345",
    "data": {
      "ssn": "123-45-6789",
      "phone": "+1-555-123-4567",
      "dob": "1990-01-15"
    }
  }'
Response
json
{
  "data": {
    "ingot_id": "ing_xyz789...",
    "name": "customer_12345",
    "version": 1,
    "key_count": 3,
    "created_at": 1702000000
  }
}

List Structured Ingots

GET /v1/products/structured-ingots/{vault_id}

List structured ingots in a vault as a single, non-paginated page.

Query Parameters

ParameterTypeRequiredDescription
limit integer Optional Maximum number of ingots to fetch before filtering (1-500). This bounds a single fetch across all ingot types in the vault; only structured ingots are then returned, so count can be smaller than the true number of structured ingots (even zero) when the vault also holds many file ingots. There is no pagination cursor, so raise the limit if you need more coverage.Default: 100

Response

FieldTypeDescription
ingots array Array of structured ingot summaries
ingots[].ingot_id string Ingot identifier
ingots[].name string Ingot name
ingots[].version integer|null Always null in list responses. Versions are only decoded on single-ingot GET
ingots[].key_count integer|null Always null in list responses. Key counts are only decoded on single-ingot GET
ingots[].created_at integer Creation timestamp
ingots[].updated_at integer|null Last update timestamp
count integer Number of ingots returned

Get Ingot Metadata

GET /v1/products/structured-ingots/{vault_id}/{ingot_id}

Get metadata and list of keys (without values).

Response

FieldTypeDescription
ingot_id string Ingot identifier
name string Ingot name
version integer Current version
key_count integer Number of keys
keys array List of all key names
created_at integer Creation timestamp
updated_at integer Last update timestamp

Example

Request
bash
curl https://api.sparkvault.com/v1/products/structured-ingots/vlt_abc123/ing_xyz789 \
  -H "X-API-Key: sv_live_xxx" \
  -H "X-Vault-Access-Token: YOUR_VAT"
Response
json
{
  "data": {
    "ingot_id": "ing_xyz789...",
    "name": "customer_12345",
    "version": 3,
    "key_count": 5,
    "keys": ["ssn", "phone", "dob", "email", "address"],
    "created_at": 1702000000,
    "updated_at": 1702100000
  }
}

Get Values as Sparks

GET /v1/products/structured-ingots/{vault_id}/{ingot_id}?keys=k1,k2&ttl=900

Retrieve specific values. Values are returned as Spark IDs for secure, auditable access.

Query Parameters

ParameterTypeRequiredDescription
keys string Required Comma-separated list of keys to retrieve (max 100 keys per request)
ttl integer Optional Spark TTL in seconds (min: 60, max: 86400)Default: 900

Response

FieldTypeDescription
ingot_id string Ingot identifier
values object Map of key to Spark info
values[key].spark_id string Spark ID containing the value
values[key].expires_at integer When the Spark expires
missing_keys array Keys that were requested but not found

Example

Request
bash
curl "https://api.sparkvault.com/v1/products/structured-ingots/vlt_abc123/ing_xyz789?keys=ssn,phone&ttl=300" \
  -H "X-API-Key: sv_live_xxx" \
  -H "X-Vault-Access-Token: YOUR_VAT"
Response
json
{
  "data": {
    "ingot_id": "ing_xyz789...",
    "values": {
      "ssn": {
        "spark_id": "spk_abc123...",
        "expires_at": 1702000300
      },
      "phone": {
        "spark_id": "spk_def456...",
        "expires_at": 1702000300
      }
    },
    "missing_keys": []
  }
}
Reading Spark Values

Use the returned Spark IDs with the Sparks API to read the actual values:

GET /v1/sparks/spk_abc123

Update Keys (PATCH)

PATCH /v1/products/structured-ingots/{vault_id}/{ingot_id}

Atomically set or delete keys. Operations are applied server-side.

Request Body

ParameterTypeRequiredDescription
set object Optional Keys to add or update (key: value pairs)
delete array Optional Key names to delete
At Least One Operation, Optimistic Concurrency

A PATCH with neither set nor delete returns 400 VALIDATION_ERROR ("At least one set or delete operation required"). Concurrent PATCHes to the same ingot are serialized via optimistic locking: the losing request receives 412 PRECONDITION_FAILED. Re-read the ingot and retry.

Response

FieldTypeDescription
ingot_id string Ingot identifier
version integer New version number
key_count integer Updated key count
updated_at integer Update timestamp

Example

Request
bash
curl -X PATCH https://api.sparkvault.com/v1/products/structured-ingots/vlt_abc123/ing_xyz789 \
  -H "X-API-Key: sv_live_xxx" \
  -H "X-Vault-Access-Token: YOUR_VAT" \
  -H "Content-Type: application/json" \
  -d '{
    "set": {
      "email": "updated@example.com",
      "address": "123 New Street"
    },
    "delete": ["old_field"]
  }'
Response
json
{
  "data": {
    "ingot_id": "ing_xyz789...",
    "version": 4,
    "key_count": 6,
    "updated_at": 1702100500
  }
}

Delete Ingot

DELETE /v1/products/structured-ingots/{vault_id}/{ingot_id}

Permanently delete a structured ingot and all its data.

Response

FieldTypeDescription
204 status No Content - ingot deleted successfully

Name Addressing

Structured ingots can be addressed by name using a colon prefix instead of the ingot ID. This allows deterministic addressing based on your own identifiers.

Path Addressing
/structured-ingots/vlt_abc/ing_xyz789 By ingot ID
/structured-ingots/vlt_abc/:customer_12345 By name (colon prefix)
Idempotent Lookups

Name addressing allows you to use your own identifiers (customer IDs, user IDs) without needing to store SparkVault ingot IDs in your database. Because a create reuses an existing same-named ingot, two structured ingots in a vault never share a name, so :name addressing is unambiguous for them. A 409 CONFLICT arises only when a same-named ingot also exists in a different folder (for example a non-structured file ingot), in which case use the ingot ID instead.

Complete Example

javascript Working with Structured Ingots
// Create a structured ingot for a customer
const createResponse = await fetch(
  `https://api.sparkvault.com/v1/products/structured-ingots/${vaultId}`,
  {
    method: 'POST',
    headers: {
      'X-API-Key': apiKey,
      'X-Vault-Access-Token': vat,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: 'customer_12345',
      data: {
        ssn: '123-45-6789',
        phone: '+1-555-123-4567',
        dob: '1990-01-15'
      }
    })
  }
);

const ingot = (await createResponse.json()).data;
console.log('Created:', ingot.ingot_id);

// Later: Retrieve SSN as a Spark (by name)
const readResponse = await fetch(
  `https://api.sparkvault.com/v1/products/structured-ingots/${vaultId}/:customer_12345?keys=ssn`,
  {
    headers: {
      'X-API-Key': apiKey,
      'X-Vault-Access-Token': vat
    }
  }
);

const { values } = (await readResponse.json()).data;
const ssnSparkId = values.ssn.spark_id;

// Read the Spark to get the actual SSN value
const sparkResponse = await fetch(
  `https://api.sparkvault.com/v1/sparks/${ssnSparkId}`,
  {
    headers: { 'X-API-Key': apiKey }
  }
);

const ssn = (await sparkResponse.json()).data.payload;
console.log('SSN:', ssn);

// Update with new fields
await fetch(
  `https://api.sparkvault.com/v1/products/structured-ingots/${vaultId}/:customer_12345`,
  {
    method: 'PATCH',
    headers: {
      'X-API-Key': apiKey,
      'X-Vault-Access-Token': vat,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      set: { email: 'new@example.com' },
      delete: ['old_field']
    })
  }
);

Constraints

Limit Value
Max keys per ingot 10,000
Max key name length 256 characters
Max value size (storage) 1 MB
Max wrapped value size (Spark retrieval) 256,000 bytes
Max keys per retrieval request 100
Max ingot name length 255 characters
Retrieval Size Cap

The 1 MB limit applies to storing a value. Retrieval returns values wrapped in Sparks, whose payloads are capped at 256,000 bytes. A value whose base64/JSON-wrapped size exceeds that cap stores fine but fails retrieval with 400 VALIDATION_ERROR. Keep individual values under ~256 KB wrapped if you need to retrieve them via the API.

Error Reference

Error Responses

StatusCodeDescription
400 VALIDATION_ERROR Invalid request parameters, missing X-Vault-Access-Token header, no set/delete operation on PATCH, or the operation would exceed the 10,000-key limit
401 AUTHENTICATION_ERROR Missing or invalid credentials, or the Vault Access Token is invalid
403 FORBIDDEN Expired VAT, VAT issued for a different vault or account, or the ingot is not managed by the Structured Ingots product
404 NOT_FOUND Vault or ingot not found
409 CONFLICT Ambiguous :name reference: a same-named ingot also exists in another folder (for example a non-structured file ingot). Use the ingot ID instead
412 PRECONDITION_FAILED Ingot changed while preparing the update. Re-read and retry the PATCH
429 RATE_LIMIT_EXCEEDED Rate limit exceeded (300 operations per minute per account)