Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
325bfd9
SK-3061: revert fields->tokens rename, restore data field on bulkInse…
Devesh-Skyflow Aug 13, 2026
ff3d69d
SK-3061: fix flowvault README inaccuracies, drop misleading sample, a…
Devesh-Skyflow Aug 13, 2026
e493d24
SK-3061: fix inaccurate JSON response examples in flowvault README
Devesh-Skyflow Aug 13, 2026
34f3a24
SK-3061: document schema vs. schemaless vault support per operation
Devesh-Skyflow Aug 13, 2026
547b2c4
SK-3061: cover InsertResponseRecord's deprecated constructor directly
Devesh-Skyflow Aug 13, 2026
c203add
Revert unsubstantiated metadata content claim in flowvault README
Devesh-Skyflow Aug 13, 2026
61c5e39
SK-3061: repeat vault-type applicability inline on each bulk operatio…
Devesh-Skyflow Aug 13, 2026
0c34f5c
SK-3061: trim vault-type notes to match the terse delete-tokens style
Devesh-Skyflow Aug 13, 2026
146faf6
SK-3061: allowlist "codehaus" for cspell
Devesh-Skyflow Aug 13, 2026
4487acc
SK-3061: fix Skyflow.getVaultConfig() crashing on an empty vault list
Devesh-Skyflow Aug 13, 2026
91eb4c3
SK-3061: document detokenize metadata shape, now grounded in the proto
Devesh-Skyflow Aug 13, 2026
be49ebf
SK-3061: add typed Token accessor for insert response tokens
Devesh-Skyflow Aug 13, 2026
234694b
SK-3061: use typed Token accessor in samples, cover its parsing branches
Devesh-Skyflow Aug 13, 2026
8148504
SK-3061: make InsertResponseRecord.tokens typed (Map<String, List<Tok…
Devesh-Skyflow Aug 13, 2026
f326ec2
SK-3061: regenerate flowvault japicmp baseline for typed getTokens()
Devesh-Skyflow Aug 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cspell.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,6 +51,7 @@
"dotenv",
"powermock",
"mvn",
"codehaus",
"sonatype",
"jfrog",
"prekarilabs",
Expand Down
3 changes: 2 additions & 1 deletion common/src/main/java/com/skyflow/logs/InfoLogs.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -115,7 +115,8 @@ public enum InfoLogs {
DEPRECATED_UPDATE_LOG_LEVEL("[DEPRECATED] Method 'updateLogLevel()' is deprecated and will be removed in an upcoming release. Use 'setLogLevel()' instead."),
DEPRECATED_CREDENTIAL_CLIENT_ID("[DEPRECATED] Credential field 'clientID' is deprecated and will be removed in an upcoming release. Use 'clientId' instead."),
DEPRECATED_CREDENTIAL_KEY_ID("[DEPRECATED] Credential field 'keyID' is deprecated and will be removed in an upcoming release. Use 'keyId' instead."),
DEPRECATED_CREDENTIAL_TOKEN_URI("[DEPRECATED] Credential field 'tokenURI' is deprecated and will be removed in an upcoming release. Use 'tokenUri' instead.")
DEPRECATED_CREDENTIAL_TOKEN_URI("[DEPRECATED] Credential field 'tokenURI' is deprecated and will be removed in an upcoming release. Use 'tokenUri' instead."),
DEPRECATED_INSERT_FIELDS_GETTER("[DEPRECATED] Method 'getFields()' is deprecated and will be removed in an upcoming release. Use 'getTokens()' instead.")
;


Expand Down
78 changes: 62 additions & 16 deletions flowvault/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@ The `flowvault` module is a Skyflow Java SDK built for high-throughput vault ope

> Meant for **Flow DB** vaults.

> **`flowvault` is a new SDK, versioned independently of `skyvault`.** It starts at `1.0.0` while `skyvault` (`com.skyflow:skyflow-java`) is at `2.x`. The two artifacts have separate version lines, so a lower `flowvault` version number does not mean it is older or behind — it is a first release, not a downgrade. Upgrade each artifact on its own.
> **`flowvault` is a new SDK, versioned independently of `skyvault`.** It started at `1.0.0` while `skyvault` (`com.skyflow:skyflow-java`) is at `2.x`. The two artifacts have separate version lines, so a lower `flowvault` version number does not mean it is older or behind — it is a first release, not a downgrade. Upgrade each artifact on its own.

[![CI](https://img.shields.io/static/v1?label=CI&message=passing&color=green?style=plastic&logo=github)](https://github.com/skyflowapi/skyflow-java/actions)
[![License](https://img.shields.io/github/license/skyflowapi/skyflow-java)](https://github.com/skyflowapi/skyflow-java/blob/main/LICENSE)
Expand All@@ -28,6 +28,7 @@ The `flowvault` module is a Skyflow Java SDK built for high-throughput vault ope
- [Timeouts and retries](#timeouts-and-retries)
- [Logging](#logging)
- [VaultController — Bulk operations](#vaultcontroller--bulk-operations)
- [Schema vs. schemaless vaults](#schema-vs-schemaless-vaults)
- [Batching and concurrency](#batching-and-concurrency)
- [Bulk Insert](#bulk-insert)
- [Bulk Tokenize](#bulk-tokenize)
Expand DownExpand Up@@ -58,7 +59,7 @@ The `flowvault` module is a Skyflow Java SDK built for high-throughput vault ope
### Gradle users

```
implementation 'com.skyflow:skyflow-flowvault-java:1.0.0'
implementation 'com.skyflow:skyflow-flowvault-java:1.0.1'
```

### Maven users
Expand All@@ -67,7 +68,7 @@ implementation 'com.skyflow:skyflow-flowvault-java:1.0.0'
<dependency>
<groupId>com.skyflow</groupId>
<artifactId>skyflow-flowvault-java</artifactId>
<version>1.0.0</version>
<version>1.0.1</version>
</dependency>
```

Expand DownExpand Up@@ -97,7 +98,7 @@ Skyflow skyflowClient = Skyflow.builder()
VaultController vault = skyflowClient.vault();
```

`flowvault`'s `vault()` takes no arguments — it always resolves to the first vault added to the builder. Use one client per vault if you need to talk to more than one.
`vault()` with no arguments returns the controller for the first vault added to the builder. To talk to more than one vault from a single client, register each with `addVaultConfig(...)` and fetch each controller by ID: `skyflowClient.vault("<VAULT_ID>")`.

# Authenticate

Expand DownExpand Up@@ -249,6 +250,8 @@ public class InitFlowVaultClient {

Every method throws `SkyflowException` on validation errors and returns the builder for chaining.

Once built, `skyflowClient.vault()` returns the first registered vault's controller; `skyflowClient.vault("<VAULT_ID>")` returns the controller for a specific registered vault, which is how one client talks to more than one vault.

## Timeouts and retries

Each HTTP setting resolves **most specific first**: the value on `VaultConfig`, else the client-wide value on `Skyflow.builder()`, else the SDK default. Only `null` means "inherit" — an explicit `0` is a real value and overrides the level below it.
Expand DownExpand Up@@ -297,6 +300,19 @@ The SDK logs through `java.util.logging` at `LogLevel.ERROR` by default. Levels
| `bulkDeleteTokens(BulkDeleteTokensRequest)` | `BulkDeleteTokensRequest`, optional `BulkDeleteTokensOptions` | `BulkDeleteTokensResponse` | Delete many tokens in one call |
| `bulkDeleteTokensAsync(BulkDeleteTokensRequest)` | same | `CompletableFuture<BulkDeleteTokensResponse>` | Async variant of `bulkDeleteTokens` |

## Schema vs. schemaless vaults

Which of these operations makes sense depends on whether the vault is **structured** (has a schema — tables and columns) or **schemaless** (stores standalone tokens with no table structure):

| Operation | Supported on |
|---|---|
| `bulkInsert` / `bulkInsertAsync` | Structured (schema) vaults — inserts into a table's columns. |
| `bulkTokenize` / `bulkTokenizeAsync` | Schemaless vaults — tokenizes a raw value directly against named token groups, with no table involved. |
| `bulkDeleteTokens` / `bulkDeleteTokensAsync` | Schemaless vaults. |
| `bulkDetokenize` / `bulkDetokenizeAsync` | Both — detokenizing only needs the token itself, not a table, so it works regardless of which kind of vault the token came from. |

This reflects supported use cases, not something the SDK validates or blocks — nothing stops you from calling, say, `bulkTokenize` against a structured vault; it just isn't the intended usage and isn't a scenario the SDK is tested against.

Each method also accepts an optional options object (`BulkInsertOptions`, `BulkTokenizeOptions`, `BulkDetokenizeOptions`, `BulkDeleteTokensOptions`) — see [Custom Request Headers](#custom-request-headers).

A single bulk call accepts at most **10,000** records or tokens; anything larger is rejected up front with a `SkyflowException`. Under that ceiling the SDK splits the payload into batches and sends them concurrently, which is why errors from one call can carry different `requestId` values.
Expand DownExpand Up@@ -342,11 +358,13 @@ The 10,000-item ceiling per bulk call is a separate, fixed limit and is not conf

Insert many records — even across different tables — in a single call. Each record is a `BulkInsertRequestRecord` with its own `data` and, optionally, its own `tableName` and `upsert`.

> **Vault type supported:** structured (schema) vaults. See [Schema vs. schemaless vaults](#schema-vs-schemaless-vaults).

**Note:**

- `tableName` must be specified at exactly one level: either on the request (`BulkInsertRequest.builder().tableName(...)`) or on **every** record (`BulkInsertRequestRecord.builder().tableName(...)`) — not both, and not neither.
- `upsert` is optional, but wherever you supply it, it must sit at the same level as `tableName`. Request-level `tableName` pairs with request-level `upsert`; record-level `tableName` pairs with per-record `upsert`.
- `UpsertOptions` requires `uniqueColumns`. `updateType` accepts `"UPDATE"` (the default) or `"REPLACE"`.
- `UpsertOptions` requires `uniqueColumns`. `updateType` accepts `"UPDATE"` or `"REPLACE"` — if omitted, the SDK sends no `updateType` at all, and the vault treats that the same as `"UPDATE"`.

### Construct a bulk insert request

Expand DownExpand Up@@ -435,8 +453,17 @@ Sample response:
"requestId": null,
"tableName": "table1",
"skyflowId": "9fac9201-7b8a-4446-93f8-5244e1213bd1",
"fields": { "card_number": "5484-7829-1702-9110", "cardholder_name": "b2308e2a-c1f5-469b-97b7-1f193159399b" },
"hashedData": null,
"tokens": {
"card_number": [
{ "token": "5484-7829-1702-9110", "tokenGroupName": "card_number_cg" }
],
"cardholder_name": [
{ "token": "b2308e2a-c1f5-469b-97b7-1f193159399b", "tokenGroupName": "deterministic_string" },
{ "token": "f1a2b3c4-d5e6-7890-abcd-ef1234567890", "tokenGroupName": "vault_token_group" }
]
},
"data": { "card_number": "4111-1111-1111-1111", "cardholder_name": "John Doe" },
"hashedData": { "card_number": "b6e6d...c3f9" },
"httpCode": 200,
"error": null
},
Expand All@@ -445,7 +472,8 @@ Sample response:
"requestId": "a1b2c3d4-...",
"tableName": "table2",
"skyflowId": null,
"fields": null,
"tokens": null,
"data": null,
"hashedData": null,
"httpCode": 400,
"error": "Insert failed. Column email is invalid."
Expand All@@ -454,14 +482,28 @@ Sample response:
}
```

Accessors: `insertResponse.getSummary()`, `insertResponse.getRecords()`, and on each record `getIndex()`, `getTableName()`, `getSkyflowId()`, `getFields()`, `getHashedData()`, `getHttpCode()`, `getError()`, `getRequestId()`.
`getTokens()` returns `Map<String, List<Token>>` — one entry per token group configured on that column, so a column with a single token group still comes back as a one-element list, not a bare string. On the wire the API models this generically (`Object`, not a fixed type) to stay flexible, but the SDK parses it into `Token` objects before handing it back, so callers get `Token.getToken()`/`Token.getTokenGroupName()` directly with no casting required:

```java
for (Token token : record.getTokens().get("card_number")) {
System.out.println(token.getTokenGroupName() + " -> " + token.getToken());
}
```

The parser (`Token.parseTokens()`) normalizes every shape the raw wire value is known to take — a list of `{token, tokenGroupName}` entries, a single such entry not wrapped in a list, or a bare token value with no group information — into a consistent `List<Token>`, rather than throwing on an unexpected one. `getTokens()` returns `null` when the record has no tokens (e.g. a failed record).

Accessors: `insertResponse.getSummary()`, `insertResponse.getRecords()`, and on each record `getIndex()`, `getTableName()`, `getSkyflowId()`, `getTokens()`, `getData()`, `getHashedData()`, `getHttpCode()`, `getError()`, `getRequestId()`.

> **Deprecation notice:** `getFields()` is deprecated in favor of `getTokens()` — it is kept only for backward compatibility and will be removed in a future release. Update call sites to `getTokens()`.

Use `insertResponse.getRecordsToRetry()` to get back only the `BulkInsertRequestRecord`s worth resubmitting — see [Retrying the failed records](#retrying-the-failed-records).

# Bulk Tokenize

Tokenize many values in one call. Each value can be tokenized against one or more named token groups.

> **Vault type supported:** schemaless vaults. See [Schema vs. schemaless vaults](#schema-vs-schemaless-vaults).

### Construct a bulk tokenize request

```java
Expand DownExpand Up@@ -538,6 +580,8 @@ Tokenize reports at **two** levels: one entry per input value in `records`, and

Detokenize many tokens in one call, optionally overriding the redaction applied per token group via `tokenGroupRedactions`.

> **Vault type supported:** both. See [Schema vs. schemaless vaults](#schema-vs-schemaless-vaults).

### Construct a bulk detokenize request

```java
Expand DownExpand Up@@ -592,7 +636,7 @@ Sample response:
"requestId": null,
"value": "4111111111111111",
"tokenGroupName": "card_number_cg",
"metadata": {},
"metadata": { "table": "table1", "skyflowId": "9fac9201-7b8a-4446-93f8-5244e1213bd1" },
"httpCode": 200,
"token": "5479-4229-4622-1393",
"error": null
Expand All@@ -617,6 +661,8 @@ Use `detokenizeResponse.getTokensToRetry()` to get back only the tokens worth re

Delete many tokens in one call.

> **Vault type supported:** schemaless vaults. See [Schema vs. schemaless vaults](#schema-vs-schemaless-vaults).

### Construct a bulk delete tokens request

```java
Expand DownExpand Up@@ -671,16 +717,16 @@ To include custom HTTP headers on an outgoing bulk request, pass a `RequestInter

| `CustomHeaderKey` | HTTP header name |
|---|---|
| `SkyflowAccountId` | `x-skyflow-account-id` |
| `SkyflowAccountName` | `x-skyflow-account-name` |
| `RequestIdHeader` | `x-request-id` |
| `SKYFLOW_ACCOUNT_ID` | `x-skyflow-account-id` |
| `SKYFLOW_ACCOUNT_NAME` | `x-skyflow-account-name` |
| `REQUEST_ID_HEADER` | `x-request-id` |

```java
import com.skyflow.enums.CustomHeaderKey;
import com.skyflow.vault.data.BulkInsertOptions;

BulkInsertOptions options = BulkInsertOptions.builder()
.interceptor(context -> context.addHeader(CustomHeaderKey.RequestIdHeader, "<YOUR_REQUEST_ID>"))
.interceptor(context -> context.addHeader(CustomHeaderKey.REQUEST_ID_HEADER, "<YOUR_REQUEST_ID>"))
.build();

BulkInsertResponse insertResponse = vault.bulkInsert(insertRequest, options);
Expand DownExpand Up@@ -721,7 +767,7 @@ Every bulk response exposes `getSummary()` and `getRecords()`. The records list
| `getError()` | failures only | Error message for this item. `null` means this item succeeded. |
| `getRequestId()` | failures only | The `x-request-id` of the batch this item was in — quote it in support escalations. Items from the same batch share one id. |

The success payload sits alongside those fields on the same object: `getSkyflowId()`/`getFields()` for insert, `getValue()`/`getTokenGroupName()`/`getMetadata()` for detokenize, `getTokens()` for tokenize, `getToken()` for delete.
The success payload sits alongside those fields on the same object: `getSkyflowId()`/`getTokens()`/`getData()` for insert (`getFields()` is a deprecated alias for `getTokens()`), `getValue()`/`getTokenGroupName()`/`getMetadata()` for detokenize, `getTokens()` for tokenize, `getToken()` for delete.

Summaries per operation:

Expand DownExpand Up@@ -805,7 +851,7 @@ vault.bulkInsertAsync(insertRequest)
|---|---|---|
| HTTP status code | `getHttpCode()` | Integer status code (e.g. `400`, `404`, `500`). |
| Message | `getMessage()` | Human-readable description of the error. |
| HTTP status string | `getHttpStatus()` | Status string from the server (e.g. `"BAD_REQUEST"`). |
| HTTP status string | `getHttpStatus()` | Status string from the server (e.g. `"Bad Request"` for a client-side validation error; for API errors, whatever string the server returns). |
| gRPC code | `getGrpcCode()` | gRPC status code from the server. |
| Request ID | `getRequestId()` | The `x-request-id` header — useful for support escalations. |
| Details | `getDetails()` | `JsonArray` of additional error context from the server. Empty array for validation errors, `null` if the server response omitted the field. |
Expand Down
Binary file modifiedflowvault/api-report/skyflow-flowvault-java.baseline.jar
Binary file not shown.
Loading
Loading