cargo-storage
Work with the data inside a Cargo workspace — models (Companies, Contacts, Deals…), datasets, columns, relationships, records, and SQL over workspace storage. Triggers: "what models do I have", "show me the schema", "add a column for", "how many contacts do I have", "SELECT … FROM", "query my compan
By getcargohq · 6,752 installs
npx skills add getcargohq/cargo-skills --skill cargo-storage
Source repository · Upstream listing
Cargo CLI — Storage
Data layer management: inspecting and modifying models, datasets, columns, relationships, unification, and records, and running SQL queries against workspace storage.
See references/response shapes.md for full JSON response structures.
See references/troubleshooting.md for common errors and how to fix them.
See references/examples/models.md for model CRUD, DDL inspection, and schema discovery examples.
See references/examples/datasets.md for dataset listing and navigation examples.
See references/examples/columns.md for column creation and management examples.
See references/examples/queries.md for storage query execute / storage query download SQL examples (WHERE, aggregations, joins, pagination, exports).
See references/examples/ingest webhook.md for ingest (webhook fed) models — deriving the webhook URL and POSTing records.
Bootstrap
Already signed in ( cargo ai whoami returns a workspace)? Skip to the next section.
Every command prints JSON to stdout; failures exit non zero with {"errorMessage": "..."} . Anything that creates a run or a batch is async — pass wait until finished or poll the matching get . When the full skill bundle is installed, [ ../cargo/references/prerequisites.md ](../cargo/references/prerequisites.md) adds the CLI version pin, token scopes, and the admin only surface.
Discover resources first
Always list before inspecting or modifying.
Retrieve in the UI: models live at app.getcargo.io/workspaces/<WORKSPACE UUID /models/<MODEL UUID . Get <WORKSPACE UUID from cargo ai whoami under workspace.uuid .
Quick reference
Models
Models are structured tables in your workspace (e.g. Companies, Contacts).
Querying: Use cargo ai storage query execute "<sql " (or storage query download query "<sql " for full exports) to run SQL against storage. Tables are referenced as <datasetSlug .<modelSlug (e.g. default.companies ) and rewritten to the underlying storage table under the hood. See [Query with SQL]( query with sql) below.
Ingest models (webhook fed)
A model whose extractor has mode.kind === "ingest" — http.listenHook and
friends — is filled by pushing records to Cargo. The app shows a "Webhook URL"
on the model settings screen; no CLI command or API field returns it , but it's
assembled from values the CLI already exposes:
Check the extractor's mode first — when it reports "autoIngest": true (calendly,
smartlead, instantlyV2, heyReach, cargo signals) Cargo registers the
hook with the provider itself and the URL must not be handed out. Full flow,
payload shapes, and limits: references/examples/ingest webhook.md .
Datasets
Datasets are logical groupings of models.
Columns
Columns define the schema of a model.
Column types: string , number , boolean , date , object , array , vector , any .
Column kinds: custom (user defined), computed (expression over other columns), metric (aggregated from a related model), lookup (single field pulled from a related model via a join).
Preview what you built
A column list doesn't tell the user whether the model is right — rows do. Two checkpoints (the pack wide convention lives in [ ../cargo/references/interaction.md ](../cargo/references/interaction.md) §4):
1. Right after model create / column create — show the schema, not rows. A new model is empty; a LIMIT 10 here returns nothing and reads as failure. Echo the columns as a compact table instead (column, type, what will fill it).
2. As soon as data lands — show the rows. After a batch, play, or import writes into the model, preview it:
Show ~10 rows and only the columns that carry meaning. Storage queries are free, so this costs nothing but a few lines of output — and it's the first moment the user can actually see what they built. When a play fills a new column, preview that column next to the record's identifying fields ( name , domain ) so filled vs. empty is obvious.
If the preview comes back empty or all null when it shouldn't, that's a finding — surface it rather than reporting the write as a success. See [ cargo diagnostics ](../cargo diagnostics/SKILL.md) to trace why.
Relationships
Relationships link models together (e.g. Contacts belong to Companies). They are
authored from the CLI, not just the UI.
relationship list takes no flags — it returns every relationship in the
workspace. Filter client side on fromModelUuid / toModelUuid .
relationship set replaces the dataset's whole relationship set. It takes a
dataset and the complete list that should exist within it: entries carrying a
uuid are updated, entries without one are created, and any existing
relationship whose uuid is absent from the payload is deleted . Sending one
relationship to a dataset that has five removes the other four. Always list
first, then send back the full array with your addition:
relation is oneToOne , manyToOne , or oneToMany . Both models must live in
the dataset you pass — relationships never span datasets, so fromDatasetUuid
and toDatasetUuid on the response always equal dataset uuid .
Failure reasons: datasetNotFound ; invalidRelationships (a column slug or
model UUID that doesn't resolve, or a duplicate — including the same pair stated
in reverse); modelNotCompatible (see below).
Unify models refuse manual relationships. In the native dataset, a unify
model's relationships are generated during sync, so naming one as fromModelUuid
or toModelUuid returns modelNotCompatible . Those auto generated rows are also
excluded from the replace above, so a set call cannot delete them.
Unification
Unification is what merges records from several source models into one canonical
account/contact — and it is configurable from the CLI , via unification on
model update . Pass null to clear it.
Field Applies to Meaning
source both integration (connector defined) or custom
type custom account , contact , accountEvent , contactEvent
uniqueColumns custom Match keys — {slug, reference} per column. This is what decides which rows are the same entity
selectedColumnSlugs custom Columns carried into the unified model. Omit for all
timeColumnSlug custom Event timestamp — for the two Event types
parent custom Links contacts/events to their account: {"kind":"model","columnSlug":…,"parentModelUuid":…} or {"kind":"reference","columnSlug":…,"reference":…}
filter custom Segmentation filter restricting which rows unify — same conjonction shape as segments
Writing the config does not recompute anything. The unified rows are rebuilt
by the model's sync run, so follow the update with a run and poll it:
Get the current config from storage model get <uuid → unification ( null
when the model doesn't unify). Once the run finishes, check the row count with
storage query execute before treating the change as done — a too narrow
uniqueColumns under merges and a too broad one collapses distinct entities, and
both look like a successful run.
Records
For advanced record queries (filtering, sorting, pagination), use segmentation segment fetch from the cargo orchestration skill.
Query with SQL
Run SQL against workspace storage with storage query execute . Tables are referenced as <datasetSlug .<modelSlug (e.g. default.companies ) and rewritten to the underlying storage table under the hood — no DDL lookup is needed for the table name.
For full exports, use storage query download — it returns a signed URL to a CSV (default) or Parquet file:
Get column slugs from storage column list model uuid <uuid (or run storage model get ddl <model uuid for the full schema and SQL dialect). Page through large result sets with LIMIT / OFFSET directly in the SQL.
See references/examples/queries.md for WHERE clauses, aggregations, joins, date queries, pagination, and the failure shapes returned on error.
Help
Every command supports help :