Marketplace
Publish one of your project's tools or agents for other projects to install, and install what others publish.
Overview
A listing is a tool or an agent a project offers to other projects. An install is another project using it: once installed, the project names the tool or agent by its id like one of its own. Every call runs on the publisher's configuration — its endpoint, secrets, instructions and provider — and the records and usage stay in the installing project. A publisher may price its listing; see Pricing.
See the OpenAPI specs for listings and installs, or browse them under API Reference → Listings and API Reference → Installs.
Data Model
Listing
| Field | Type | Description |
|---|---|---|
id | string | Public listing ID (lst_ prefix). |
project_id | string | The publishing project. Not shown in the marketplace. |
resource_type | tool | agent | What the listing publishes. |
resource_id | string | The published tool or agent. |
title | string | Up to 120 characters. |
description | string, nullable | Up to 2,000 characters. |
state | draft | in_review | listed | suspended | See Publishing. |
pricing | array | What an installing project is charged per call. Empty is free. See Pricing. |
pricing_next | object, nullable | A price increase waiting out its notice: effective_from and the new pricing. |
created_at | string (date-time) | |
updated_at | string (date-time) |
A public listing carries interface instead of project_id: what an installing
project sees of the resource.
Install
| Field | Type | Description |
|---|---|---|
id | string | Public install ID (ins_ prefix). |
project_id | string | The installing project. |
listing_id | string | The installed listing. |
resource_type | tool | agent | |
resource_id | string | The id the project names the resource by. |
state | active | suspended | blocked | See Suspending and blocking. |
installed_at | string (date-time) | |
updated_at | string (date-time) |
The publisher's view of an install carries project_name, calls_cycle,
errors_cycle and sampled_at instead: the installing project's calls through
the listing this billing cycle, sampled every 15 minutes. It never carries what
those calls contained.
Key Concepts
Publishing
Any project member can publish. A listing starts as draft; submitting it
moves it to in_review, and naturali either lists it in the marketplace or sends
it back to draft. A draft or in_review listing installs by its id, so you
can try it from a second project before it is listed.
An agent on a naturali model must have a price: a listing on an unpriced model
answers 503 model_not_priced, and so does changing a listed agent's provider
or model to one. An agent on a provider of your own has no such check.
What an installing project sees
| Type | interface | Where it can be used |
|---|---|---|
| Tool | id, name, description, parameters | An agent's tool bindings, an ingestion rule, a direct call |
| Agent | id, name | A direct generation, a conversation, a session, an ingestion rule as converter |
Never instructions, provider, headers or secrets. interface is read live, so
a change to the tool shows on the next read.
The installing project pays for its runs and for the model, like any generation
of its own, plus the listing's price. An installed agent on a naturali model is
refused like your own: 402 insufficient_credit while credit_balance_usd is
negative, 503 model_not_priced on a model with no price. Its model usage is
billed even if you uninstall it before the next reconciliation.
Pricing
A price is a list of components, each { component, unit, quantity, unit_price }.
quantity is JSON Logic giving how many units a call
uses; leave it null to charge one per call. It can read:
| Type | What quantity reads |
|---|---|
| Tool | input, action, response, outcome, duration_ms |
| Agent | response.usage, response.cost_usd, response.steps, response.tool_calls, response.stop_reason, outcome |
Anything else is refused with 400, naming the field. component cannot be a
name the platform already meters, such as input_tokens or tool_call.
- A failed call is never charged a component. It still counts as a run, and any model cost is still paid.
- What pays a fee. Only purchased credit and marketplace earnings that model
usage has not spent, never a plan's included credit or granted credit. Usage
spends included credit first, then granted, then purchased. A call to a
priced listing, a generation on an agent that has one among its tools, or an
ingest through an ingestion rule naming one, answers
402 insufficient_creditwithdetails.balance: "paid"whilepaid_balance_usdis zero or less. When fees take it below zero, the schedule triggers and orchestration runs that name a priced listing are paused, and can be restored once it is positive again. - What the publisher earns. 90% of each fee the consumer paid, credited to
the publisher's
earned_balance_usdevery 15 minutes. A fee the consumer has not yet paid for is credited once they buy credit. - Changing a price. A lower price applies the next minute. A higher unit
price or a new component applies 7 days later: until then the listing returns
it in
pricing_next, and every installing project's billing owner gets an email. A component you leave out is no longer charged. A listing nobody has installed changes price the next minute either way.
Suspending and blocking
- Suspending a listing stops it in every installing project at once: an
agent runs without the tool, and a converter fails with
CONVERTER_FAILED. Installs are kept, and listing it again resumes them with no reinstall. A listing naturali suspended cannot be resumed by its publisher. - Removing one install blocks that project: its install becomes
blockedand installing again answers403 install_blocked. Removing the blocked install lets it install again. - A listing whose tool or agent is deleted is suspended within the hour.
The installing project's billing owner gets an email when a listing is suspended, at most one a day per listing, and whenever its access is removed.
Uninstalling
While agents, ingestion rules or other resources still name the installed
resource, uninstalling answers 409 install_in_use with them in
details.references. force=true uninstalls anyway, and those resources stop
reaching it.
Limits
- An installed tool has no version: a change by its publisher applies to every installing project.
- An installed agent runs its publisher's current release. Each generation records the version it ran.
- An installed agent cannot be bound as a tool of another agent.
Managed conversion
The converter behind managed conversion is
naturali's own listing, installed in every project with conversion on. It
cannot be installed by hand (403 managed_resource_read_only), and while
conversion is on its install is removed by turning conversion off, not by
uninstalling.
Answer from images and audio
lists the rules that name it.
Who may do what
Every project route needs any project member. Browsing the marketplace needs only a signed-in caller.
Examples
Publish a tool and submit it
- CLI
- SDK
- curl
naturali create-listing \
--project-id proj_V1StGXR8Z5jdHi6B \
--resource-type tool \
--resource-id tool_V1StGXR8Z5jdHi6B \
--title "Invoice OCR"
naturali submit-listing \
--project-id proj_V1StGXR8Z5jdHi6B \
--listing-id lst_V1StGXR8Z5jdHi6B
const { data: listing } = await naturali.listings.createListing({
path: { project_id: 'proj_V1StGXR8Z5jdHi6B' },
body: {
resource_type: 'tool',
resource_id: 'tool_V1StGXR8Z5jdHi6B',
title: 'Invoice OCR',
},
});
await naturali.listings.submitListing({
path: { project_id: 'proj_V1StGXR8Z5jdHi6B', listing_id: listing!.id },
});
curl -X POST https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/listings \
-H "Authorization: Bearer $NATURALI_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "resource_type": "tool", "resource_id": "tool_V1StGXR8Z5jdHi6B", "title": "Invoice OCR" }'
curl -X POST https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/listings/lst_V1StGXR8Z5jdHi6B:submit \
-H "Authorization: Bearer $NATURALI_TOKEN"
Browse the marketplace and install
GET /v1/listings, then
POST /v1/projects/{project_id}/installs.
- CLI
- SDK
- curl
naturali list-public-listings
naturali create-install \
--project-id proj_6NgFz3xXy2kPq9Wd \
--listing-id lst_V1StGXR8Z5jdHi6B
const { data: listings } = await naturali.listings.listPublicListings();
const { data: install } = await naturali.installs.createInstall({
path: { project_id: 'proj_6NgFz3xXy2kPq9Wd' },
body: { listing_id: listings!.data[0]!.id },
});
curl https://api.naturali.ai/v1/listings \
-H "Authorization: Bearer $NATURALI_TOKEN"
curl -X POST https://api.naturali.ai/v1/projects/proj_6NgFz3xXy2kPq9Wd/installs \
-H "Authorization: Bearer $NATURALI_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "listing_id": "lst_V1StGXR8Z5jdHi6B" }'
The installed tool is then named by its resource_id in an agent's tool
bindings, like any tool of the project's own.
See who installed your listing
- CLI
- SDK
- curl
naturali list-listing-installs \
--project-id proj_V1StGXR8Z5jdHi6B \
--listing-id lst_V1StGXR8Z5jdHi6B
const { data: installs } = await naturali.listings.listListingInstalls({
path: {
project_id: 'proj_V1StGXR8Z5jdHi6B',
listing_id: 'lst_V1StGXR8Z5jdHi6B',
},
});
curl https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/listings/lst_V1StGXR8Z5jdHi6B/installs \
-H "Authorization: Bearer $NATURALI_TOKEN"
Price a listing
- CLI
- SDK
- curl
naturali update-listing \
--project-id proj_V1StGXR8Z5jdHi6B \
--listing-id lst_V1StGXR8Z5jdHi6B \
--pricing '[{ "component": "page", "unit": "count", "quantity": { "var": "response.page_count" }, "unit_price": 0.002 }]'
await naturali.listings.updateListing({
path: {
project_id: 'proj_V1StGXR8Z5jdHi6B',
listing_id: 'lst_V1StGXR8Z5jdHi6B',
},
body: {
pricing: [
{
component: 'page',
unit: 'count',
quantity: { var: 'response.page_count' },
unit_price: 0.002,
},
],
},
});
curl -X PATCH https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/listings/lst_V1StGXR8Z5jdHi6B \
-H "Authorization: Bearer $NATURALI_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "pricing": [{ "component": "page", "unit": "count", "quantity": { "var": "response.page_count" }, "unit_price": 0.002 }] }'
Suspend and resume a listing
- CLI
- SDK
- curl
naturali update-listing \
--project-id proj_V1StGXR8Z5jdHi6B \
--listing-id lst_V1StGXR8Z5jdHi6B \
--state suspended
naturali update-listing \
--project-id proj_V1StGXR8Z5jdHi6B \
--listing-id lst_V1StGXR8Z5jdHi6B \
--state listed
const path = {
project_id: 'proj_V1StGXR8Z5jdHi6B',
listing_id: 'lst_V1StGXR8Z5jdHi6B',
};
await naturali.listings.updateListing({ path, body: { state: 'suspended' } });
await naturali.listings.updateListing({ path, body: { state: 'listed' } });
curl -X PATCH https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/listings/lst_V1StGXR8Z5jdHi6B \
-H "Authorization: Bearer $NATURALI_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "state": "suspended" }'
Block a project, then let it back
DELETE /v1/projects/{project_id}/listings/{listing_id}/installs/{install_id}
blocks the install; the same call on the blocked install removes it.
- CLI
- SDK
- curl
naturali delete-listing-install \
--project-id proj_V1StGXR8Z5jdHi6B \
--listing-id lst_V1StGXR8Z5jdHi6B \
--install-id ins_V1StGXR8Z5jdHi6B
await naturali.listings.deleteListingInstall({
path: {
project_id: 'proj_V1StGXR8Z5jdHi6B',
listing_id: 'lst_V1StGXR8Z5jdHi6B',
install_id: 'ins_V1StGXR8Z5jdHi6B',
},
});
curl -X DELETE https://api.naturali.ai/v1/projects/proj_V1StGXR8Z5jdHi6B/listings/lst_V1StGXR8Z5jdHi6B/installs/ins_V1StGXR8Z5jdHi6B \
-H "Authorization: Bearer $NATURALI_TOKEN"
Uninstall
- CLI
- SDK
- curl
naturali delete-install \
--project-id proj_6NgFz3xXy2kPq9Wd \
--install-id ins_V1StGXR8Z5jdHi6B \
--force true
await naturali.installs.deleteInstall({
path: {
project_id: 'proj_6NgFz3xXy2kPq9Wd',
install_id: 'ins_V1StGXR8Z5jdHi6B',
},
query: { force: true },
});
curl -X DELETE "https://api.naturali.ai/v1/projects/proj_6NgFz3xXy2kPq9Wd/installs/ins_V1StGXR8Z5jdHi6B?force=true" \
-H "Authorization: Bearer $NATURALI_TOKEN"