Encrypted records, read one field at a time

Keep a customer's SSN and address as one sealed record in your Vault, filed under the ID you already use for them. Your app names the fields it needs and no other field is sent. Each value arrives as a Spark that burns on its first read, and a write built on stale data is refused. SparkVault's own sign-in keeps every person this way.

Your endpoint: https://api.sparkvault.com/v1/products/structured-ingots/{vault_id}

A read of two fields from one Structured Ingot The Structured Ingot named customer_12345, at version 7, holds six fields with their values sealed: email, phone, address, dob, ssn and plan. A GET names email and ssn with a ttl of 300 seconds, and two Sparks are returned, one per value, each readable once and expiring in 5 minutes. :customer_12345 6 fields, version 7, all values sealed email phone address dob ssn plan GET .../:customer_12345 ?keys=email,ssn&ttl=300 email ssn spk_9f2... spk_b2d... reads once reads once expires in 5 min expires in 5 min
The read names two of six fields. The other four stay in the record.

Where a record lives, and what a read returns

A Structured Ingot is one customer's record: named fields and their values, sealed as one Ingot in your Vault. Your app reaches it by the ID it already holds for that customer.

What a Structured Ingot holds, how a read and a write behave, who can open one, and the SparkVault product that runs on them
Part What happens What to know
One record per person Your app creates one Structured Ingot per customer, up to 10,000 fields in it, and names it with the ID it already uses for them, such as :customer_12345. A name the Vault already holds is refused, so a second create cannot replace the first. Change a record with a PATCH, and read or delete it by the same name.
A read names its fields A read asks for the fields it needs, up to 100 at a time, and gets one Spark per field found. The response carries nothing else. Your code reads each Spark once to get the value. A second read is turned away, and a Spark nobody reads expires, after 15 minutes unless the read set another lifetime.
A write that cannot clobber A PATCH sets and deletes fields in one request, and lands only if the record is still the version it was built from. When two writers race, one lands and the other is refused with nothing written. It reads the record again and rebuilds its change from the current version.
Keys and seats Every request carries your API key and a Vault Access Token from a Vault you have unsealed. Writing a record takes a Full License seat. View Only members can read. Each create and each read is written to the record's audit log, and each delete to the Vault's, under the API key or member who asked, with the address and client the request came from.
Secure Identity SparkVault's own sign-in product keeps each person's profile, verified emails and phones, and passkeys as one Structured Ingot, and reads and patches it through this API.

Every person who signs in to SparkVault is kept as one of these records.

See secure identity
Fields in one record
10,000
Each field name up to 256 characters.
Text in one value
1 MB
A value can also be a number, a boolean, a list or an object.
Fields in one read
100
Each value found becomes its own Spark.
Characters in a record's name
255
Unique among the Vault's Structured Ingots.

Each value comes back as a Spark you read once

The answer to a read holds no values. It holds one Spark ID per field it found, and your code reads each Spark once, with your API key, to get the value. That read burns the Spark. Ask again and the answer is 403, and a Spark nobody reads expires when its time runs out.

Lifetime
From one minute to 24 hours, set in seconds with ttl. 15 minutes when you set none.
Sealing
Each Spark has its own AES-256-GCM key, derived from SparkVault's key and your account's key.
A read of two fields at your endpoint, then one Spark read. The value arrives and the Spark is ash.
GET /vlt_abc123/:customer_12345
    ?keys=email,ssn&ttl=300
X-API-Key: sv_live_...
X-Vault-Access-Token: vat_...

{
  "data": {
    "ingot_id": "ing_4e1...",
    "version": 7,
    "values": {
      "email": {
        "spark_id": "spk_9f2...",
        "expires_at": 1702000300
      },
      "ssn": {
        "spark_id": "spk_b2d...",
        "expires_at": 1702000300
      }
    },
    "missing_keys": []
  }
}

GET /v1/sparks/spk_9f2...
X-API-Key: sv_live_...

{
  "data": {
    "spark_id": "spk_9f2...",
    "payload": "ada@example.com",
    "content_type": "text/plain",
    "status": "ash"
  }
}

A PATCH built on stale data is refused

One PATCH sets and deletes fields together. We replace the whole record only if it is still the version the PATCH was built from, so when two PATCHes race, one lands and the other gets a 412. Send the version your read returned as expected_version, and a PATCH computed from values that have changed since is refused the same way.

After a 412
Nothing was written. Read the record again, rebuild the change from what you get, and send it with the new version.
Checked first
A malformed PATCH, or one with a field name or a value the record cannot hold, stops at 400 before the record is opened.
Versions
A new Structured Ingot is version 1, and each PATCH that lands adds one.

Seven requests do the work

Each carries your API key and a Vault Access Token. Paths are relative to your endpoint, and a record is addressed by its ID or by your own name for it after a colon.

Read the Structured Ingots reference
Create
POST /{vault_id}
List
GET /{vault_id}
Read the field names
GET /{vault_id}/{ingot_id}
Read values
GET /{vault_id}/{ingot_id}?keys=email,ssn&ttl=300
Check it exists
GET /{vault_id}/{ingot_id}/info
Patch
PATCH /{vault_id}/{ingot_id}
Delete
DELETE /{vault_id}/{ingot_id}

Who can open a Structured Ingot

A Structured Ingot is an Ingot, sealed the way a file in your Vault is sealed: AES-256-GCM, under a key derived from SparkVault's key, your account's key and your Vault Master Key.

Read the security architecture
Vault Access Token
Every request carries one, minted by unsealing the Vault with its VMK or a Delegated Vault Access Key (DVAK). It lasts an hour by default and up to 24 hours.
Read-only keys
A session opened with a read-only DVAK reads records and changes none.
Kept from file routes
The Vault's file routes refuse to download, replace, rename or delete a Structured Ingot (403). Every request here that names a record refuses one this product does not manage.
At rest
To read fields or apply a PATCH, our servers decrypt the record in memory, inside the request your Vault Access Token authorizes. It is encrypted at rest, not end-to-end.

What it costs

See the pricing

Structured Ingots have no price of their own and come with every SparkVault plan. Writing one takes a Full License seat, $79 per month, which adds 25 GB of storage and 50 GB of bandwidth to your account's pools.

A record's encrypted body counts against pooled storage. Reading its fields moves the whole body, and a PATCH moves it down and the replacement back up. Those bytes count against pooled bandwidth.

Store your first record

Create a Vault, unseal it for a Vault Access Token, and POST a name and a JSON object to the Vault's Structured Ingots endpoint.