A rejected row is never counted as written
How · rejected_rows: [{ index, reason }] alongside written_rows. A partial write reads as a partial write, which is the only honest way to report it.
Integration · Warehouse
Mapped rows land in a BigQuery table via tabledata.insertAll — in batches of 500, with per-row rejections reported individually instead of hidden behind a 200.
How it works
BigQuery’s streaming insert has a trap in it: tabledata.insertAll answers HTTP 200 even when it rejected rows, listing them under insertErrors. A caller that checks only the status code reports a successful import that silently dropped records — for a regulated-data product, the worst possible failure. So written_rows counts only rows NOT named in that array, and the rejections come back individually with the reason BigQuery gave.
rejected_rows with their reasons.What you get
How · rejected_rows: [{ index, reason }] alongside written_rows. A partial write reads as a partial write, which is the only honest way to report it.
How · GET /v1/connectors/{id}/schema returns the column list and a ready-to-use template. Metadata only — it never selects row data.
How · The key file is validated by parseGcpCredentials() at save time, envelope-encrypted at rest, and masked on every read.
How · normalizeSecretField() folds every provider spelling — private_key, token, secret_access_key, account_key, service_account_json — into one auth_value field, which is KEK-envelope-encrypted before the row is written. A GET masks it to a 4-character hint. If encryption fails the field is dropped rather than stored in plaintext.
Configuration
Dataset and table are validated as bare identifiers at save time, so a name that would have to be rewritten before it could be used is refused rather than quietly reshaped.
| Key | Required | What it is |
|---|---|---|
| service_account_jsonsecret | Required | The service-account key file. Must parse and carry client_email and private_key. Stored as auth_value. |
| dataset | Required | Dataset id. Must be a bare identifier. |
| table | Required | Table id. A bare identifier, optionally dotted. Overridable per call with destination.table. |
| project_id | Optional | Project id. Falls back to the project_id inside the key file. |
In code
Hand the gateway a file, a query or a URL and a BigQuery destination. The columns come from the table, the validators run per row, and the report says exactly what landed.
/v1/connectorsSave the connector. The secret is encrypted before it reaches Postgres.session/v1/connectors/{id}/schemaRead the target’s own columns — metadata only, never row data.bearer/v1/gatewayAny input in, this destination populated, a delivery report out.bearer/v1/connectors/{id}/rotate-secretReplace the credential in place; the old one becomes unrecoverable.sessionwritten_rows excludes anything BigQuery named in insertErrors. A run that reports 4 998 wrote 4 998.dry_run validates and reports the batch count without sending.{
"kind": "bigquery",
"name": "Analytics — orders",
"config": {
"project_id": "acme-analytics",
"dataset": "ops",
"table": "orders",
"service_account_json": "{\"client_email\":\"mapr@acme-analytics.iam.gserviceaccount.com\",\"private_key\":\"-----BEGIN PRIVATE KEY-----\\n…\"}"
}
}curl https://api.adaptivmapr.com/v1/gateway \
-H "Authorization: Bearer $MAPR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": { "url": "https://acme.example.com/exports/orders.csv" },
"destination": { "connector_id": "con_bb14…" }
}'{
"schema_id": "orders_v1",
"source": "destination",
"row_count": 5000,
"destination": {
"connector_id": "con_bb14…",
"kind": "bigquery",
"table": "orders",
"schema_source": "destination",
"protocol": "bigquery",
"written_rows": 4998,
"batches": 10,
"failed_batches": [],
"rejected_rows": [
{ "index": 1204, "reason": "no such field: legacy_sku" },
{ "index": 3781, "reason": "invalid value for TIMESTAMP" }
]
}
}Limits & failure modes
| Code | When | What to do |
|---|---|---|
400 config_invalid | The key file does not parse, or dataset/table are not bare identifiers. | Checked at save time so an unusable connector is never created. |
401 auth_failed | The token exchange was refused. | Usually a revoked key or a missing role on the dataset. Google’s message is passed through. |
200 with rejected_rows | BigQuery accepted the request but rejected individual rows. | The rejects are listed with reasons and subtracted from written_rows — never reported as written. |
422 schema_destination_mismatch | A supplied schema names a column the table does not have. | Caught in pre-flight, before anything is streamed. |
Not applicable: BigQuery is a destination here, not a source. Repeat safety comes from what you send, not from a watermark — the write is a streaming insert, so de-duplication belongs in the query layer or in the input you feed the gateway.
X-PHI and X-Region to phi-cloud so a regulated run lands on an in-region, BAA-eligible model, it costs +20% on the whole charge, and it is locked until the workspace accepts the BAA in Settings → Security & Data. An explicit PHI ask without an acceptance is 403 agreement_required, never a silent downgrade. A standard run keeps the workspace’s region pin — the region decides where compute may run, and the sandbox refuses a region-less run.What it costs
Moving bytes is not a line item. A sync that pulls a file and a destination write that lands the rows are both part of one map, and the map is what the wallet sees. There is no free tier and no subscription — top up from $10, a balance shared across the phi-cloud suite.
| Charge | Rate | Notes |
|---|---|---|
| Every map | $0.001 | A flat per-map fee — a few tokens — charged even when the run was fully deterministic or hit the layout cache and used no AI at all. |
| AI, only when it ran | at cost × 2 | Layer-5 cleanup, any-to-any convert and structural reshape bill the phi-cloud tokens actually consumed. Bring your own model key and it is × 0.5. |
| PHI / enterprise routing | +20% | Multiplies the whole charge, flat fee included — and only when the run genuinely got that routing. Locked until the workspace accepts the BAA in-app. |
Questions
Verified against lib/googleProviders.ts · lib/destinations.ts · lib/introspect.ts · app/api/v1/connectors/route.ts
BigQuery is a trademark of Google LLC. Named here to describe interoperability only — no affiliation, endorsement or partnership is claimed.
Ready when you are
Start with a $10 prepaid wallet. Every map draws a few tokens; in schema-only mode only headers and a few sample rows ever leave you.