Skip to content

manifest.json is the integration registry for your Swytchcode project.

It stores metadata about every installed integration, including its version, API endpoints, authentication configuration, and execution behavior.

Unlike tooling.json, which defines what can execute, manifest.json defines how an integration executes.

The file is located at:

.swytchcode/integrations/manifest.json

Every integration installed using swy get or swy add automatically updates this file.

Both the CLI and the MCP server share this file during execution. Updates are serialized to prevent corruption when multiple tools modify it simultaneously.


What does manifest.json do?

For every installed integration, Swytchcode stores:

  • Integration version
  • Sandbox endpoint
  • Production endpoint
  • Authentication metadata
  • Method count
  • Execution policy

During execution, the kernel reads this file to determine:

  1. Which endpoint should receive the request
  2. How authentication should work
  3. Whether retries are allowed
  4. Timeout behavior
  5. Idempotency settings
  6. Concurrency limits

File Structure

A simplified example looks like this:

{
"stripe.stripe": {
"version": "v2",
"sandbox_endpoint": "https://api.stripe.com",
"production_endpoint": "https://api.stripe.com",
"methods": 42,
"auth": {
"type": "api_key",
"header": "Authorization"
}
}
}

Each top-level key represents a single integration.


Entry Fields

Each integration contains the following fields.

version

Defines the pinned version of the integration, e.g. "v2". This is the same version string that appears next to the integration in tooling.json.integrations (for example "stripe.stripe": { "version": "v2" }) - the two files must agree, since swy bootstrap and swy sync compare them to decide what to fetch.

{
"version": "v2"
}

Swytchcode always executes using the pinned version stored in the manifest, never “whatever is newest.” If you want a newer integration version, run swy get <project> again to fetch the new bundle and update this file - editing the field by hand won’t fetch anything.


sandbox_endpoint

Defines the base URL used when the project is running in sandbox mode (tooling.json.mode: "sandbox").

{
"sandbox_endpoint": "https://sandbox.example.com"
}

Many providers (Stripe is a common example) use the same host for sandbox and production and distinguish environments by which API key you authenticate with instead of by URL - in that case sandbox_endpoint and production_endpoint are identical, as shown in the Complete Example below.


production_endpoint

Defines the base URL used for production execution (tooling.json.mode: "production").

{
"production_endpoint": "https://api.example.com"
}

Because the environment is resolved from tooling.json.mode rather than from a flag on exec, switching a project from sandbox to production means changing mode in tooling.json (or re-running swy init --mode=production) once, rather than remembering to pass an environment flag on every call.


methods

Number of executable methods included in the integration bundle, e.g. 42.

{
"methods": 42
}

This value is informational - it reflects what’s available in the fetched bundle under .swytchcode/integrations/, not what’s enabled in your project. Use swy list tooling to see what’s actually been added via swy add, and swy info <canonical_id> to inspect one method in detail. It’s maintained automatically by swy get, swy bootstrap, and swy sync; don’t hand-edit it.


auth

Authentication metadata used by Swytchcode to know how to authenticate against this provider - it’s the schema, not the secret. The actual key or token is never stored here; it’s resolved separately at execution time (see Managed Authentication).

Example:

{
"auth": {
"type": "api_key",
"header": "Authorization"
}
}

Depending on the integration, authentication metadata may include:

  • Authentication type (api_key or oauth)
  • OAuth scopes
  • Authorization header name
  • Token format

For crypto exchanges the type is request_signature. The block, and the library’s init_details, then carries a signing recipe: how to sign each request, and which fields auth connect asks for. It describes the method only; the keys themselves are entered on your machine and never stored here:

{
"auth": {
"type": "request_signature",
"provider_slug": "binance",
"signing": {
"version": 1,
"credentials": [
{ "name": "api_key", "label": "API key" },
{ "name": "secret_key", "label": "Secret key", "secret": true }
],
"steps": [
{ "id": "ts", "op": "time", "unit": "ms" },
{ "id": "msg", "op": "concat", "parts": ["{query}", "{body}"] },
{ "id": "sig", "op": "hmac", "alg": "sha256", "key": "secret_key", "input": "{msg}", "output": "hex" }
],
"inject": { "query": [{ "name": "recvWindow", "value": "5000" }, { "name": "timestamp", "value": "{ts}" }] },
"set": { "headers": { "X-MBX-APIKEY": "{api_key}" }, "query": [{ "name": "signature", "value": "{sig}" }] }
}
}
}

The recipe format is described in Signed requests. A recipe using anything this CLI version doesn’t know is rejected with “upgrade the CLI”.

swy auth connect [provider] reads this block to decide whether to prompt for an API key or start an OAuth flow, and swy auth connect with no argument reads it across every installed integration to list which providers still need credentials. swy doctor also checks this block as part of validating that installed integrations are fully configured.


execution_policy

Defines how HTTP requests to this integration behave: retries, timeouts, concurrency, response-size limits, and idempotency. Every field is optional and unset fields fall back to the built-in defaults - see the full Execution Policy breakdown below for every field, its default, and when to change it.


Environment Selection

Swytchcode automatically chooses the correct endpoint using the project mode defined in tooling.json.

tooling.json
mode = sandbox
│
▼
sandbox_endpoint
tooling.json
mode = production
│
▼
production_endpoint

The final request URL is built as:

Base URL
+
Method Endpoint
=
Final Request URL

For example:

https://api.stripe.com
+
/v1/customers
=
https://api.stripe.com/v1/customers

Whenever a request executes, Swytchcode prints the active environment.

Example:

Running live - stripe (production)

This prevents accidental production requests.

If you’re not sure an endpoint is reachable before you run a real workflow against it, swy doctor --network checks every configured endpoint (sandbox and production, across all installed integrations) and reports its current status.


Providers on your own instance

Some providers have no single base URL. Jira, Salesforce, Oracle, ServiceNow and self-hosted tools run on your own host, such as https://acme.atlassian.net. Their integration can’t ship your URL, so its endpoint arrives as a placeholder for you to fill in.

An endpoint is not set when it:

  • is empty
  • has a placeholder: <your-domain>, {instance} or {{site}}
  • uses the undefined:// scheme
  • is exactly http://localhost or http://127.0.0.1, with no port and no path

A local server with a port or a path, like http://localhost:8080, is a real endpoint.

Swytchcode never sends a request to an endpoint that is not set. The call stops with exit code 1 and an error with category base_url_not_set:

{
"error": "base URL for [email protected] (production) is not set: \"https://<your-domain>.atlassian.net\" (it has a placeholder to fill in)",
"category": "base_url_not_set",
"suggested_action": "this provider runs on your own instance - set production_endpoint for \"[email protected]\" in .swytchcode/integrations/manifest.json, or set SWYTCHCODE_BASE_URL_JIRA_JIRA_PRODUCTION"
}

Only the mode the call runs in is checked, so a sandbox endpoint you haven’t set doesn’t block production calls.

--dry-run and --explain send nothing, so they still run: they print the same message as a warning and show the request against https://base-url-not-set.invalid, a host that never resolves.

Many integrations ship only a production endpoint. If your project runs in sandbox mode, set sandbox_endpoint for them too. Swytchcode doesn’t fall back to the production URL, because for most providers that would change real data.

swy doctor lists every provider whose endpoint is not set.

Set it in manifest.json

Replace the placeholder with your URL:

{
"production_endpoint": "https://acme.atlassian.net"
}
}

swy get and swy sync keep the URL you set. When you install a new version of the provider, it takes the URL from the version you already have.

Or set an environment variable

An environment variable wins over manifest.json, which suits CI and per-machine setups:

VariableUsed for
SWYTCHCODE_BASE_URL_<PROVIDER>_<LIBRARY>_PRODUCTIONproduction calls
SWYTCHCODE_BASE_URL_<PROVIDER>_<LIBRARY>_SANDBOXsandbox calls
SWYTCHCODE_BASE_URL_<PROVIDER>_<LIBRARY>both, when the mode-specific one is not set

<PROVIDER>_<LIBRARY> is the provider name in capitals with . replaced by _, so jira.jira becomes JIRA_JIRA. A name with any other character, such as -, has no variable; set it in manifest.json instead.

Terminal window
export SWYTCHCODE_BASE_URL_JIRA_JIRA_PRODUCTION=https://acme.atlassian.net

The value must follow the URL security rules below.


URL Security Rules

Swytchcode enforces URL safety automatically.

HTTPS

HTTPS is supported for every host.

https://api.stripe.com

HTTP

HTTP is only allowed for local development.

Supported hosts:

  • localhost
  • 127.0.0.1
  • ::1

Requests to external HTTP endpoints are rejected.


Self-signed certificates

For local development, TLS verification can be disabled.

Terminal window
SWYTCHCODE_INSECURE=1

This is only allowed outside CI environments.

When running in GitHub Actions, GitLab CI, or other supported CI environments, insecure TLS is always rejected.


Execution Policy

The execution_policy object controls how HTTP requests are executed for a single integration: retry attempts, retry delays, timeouts, concurrency, response-size limits, and idempotency. It lives inside that integration’s entry in manifest.json, so different integrations can have different policies - a flaky third-party API might get a higher max_retries while an internal service you fully trust gets none.

Every field is optional. You only need to set the fields you want to override; Swytchcode merges the rest with its built-in defaults (documented field-by-field below).

execution_policy is not the same as policies.json:

policies.jsonexecution_policy
Question it answersShould this call run, and in what form?How is it sent?
ScopeAny call, matched by target and conditionOne integration
ExamplesBlock, hold for approval, limit per day, change arguments, hide response dataRetries, timeouts, concurrency, idempotency, response size
When it runsBefore the call, and after the final responseWhile the call is sent, including retries

A retried call counts once toward a policy’s limits, and a policy’s RATE_LIMITED error says "source": "policy" so it can’t be mistaken for the provider’s 429. See policies.json vs execution_policy.

Example - retry more aggressively and allow more time per request for a slow provider:

{
"execution_policy": {
"max_retries": 5,
"http_timeout_ms": 60000
}
}

When you install a new version

Each installed version of a provider has its own entry. When swy get or swy sync installs a new version, the new entry starts with the same execution_policy as the versions you already have, so your settings carry over. If your installed versions have different settings, the new version starts with the defaults; copy the settings you want into it.

Checking your settings

swy doctor warns about any value Swytchcode can’t use, and says what it does instead. swy get and swy sync show the same warnings after they update the file. For example, "mode": "dynamc" is reported as not a valid mode, because with a typo no idempotency key would be sent.


Retry Configuration

max_retries

Maximum retry attempts after the first request.

Default:

3

Negative values are treated as 0.


retry_on

HTTP status codes that trigger retries.

Default:

[
429,
503,
504
]

non_retryable

Status codes that are never retried.

Default:

[
400,
401,
403,
404,
422
]

on_401

Determines what happens when authentication fails.

Supported values:

ValueDescription
failImmediately fail the request.
refresh_and_retryRefresh OAuth credentials once and retry.

Default:

{
"on_401": "fail"
}

Retry Delays

base_delay_ms

Initial retry delay.

Default:

500

(milliseconds)


max_delay_ms

Maximum retry delay.

Default:

30000

(milliseconds)


Timeouts

connect_timeout_ms

Maximum DNS and TCP connection time.

Default:

5000

http_timeout_ms

Maximum duration for an individual request.

Default:

30000

total_timeout_ms

Maximum duration including all retries.

Default:

90000

Concurrency

max_concurrent

Limits parallel requests to a provider.

Default:

5

Setting the value to 0 or a negative number disables the limiter.

The limit applies within one running process: one swy command, or one MCP server. Two separate commands running at the same time each have their own limit. To cap calls across processes, use a rate policy.


Response Size

max_response_bytes

Maximum response size before truncation.

Default:

102400

(bytes)

Values less than or equal to zero disable response truncation.


Idempotency

Mutating APIs sometimes receive duplicate requests due to retries or network failures.

The idempotency object prevents duplicate operations by attaching an idempotency key.

Example:

{
"idempotency": {
"mode": "dynamic"
}
}

mode

Determines whether idempotency is enabled.

ValueDescription
noneDisable idempotency.
dynamicAutomatically attach an idempotency key.

Default:

none

header_name

Header used for the idempotency key.

Default:

Idempotency-Key

scope

Kept so older manifests still parse. A new key is always generated for every request (call); any other value, including workflow, is ignored.


Complete Example

{
"stripe.stripe": {
"version": "v2",
"sandbox_endpoint": "https://api.stripe.com",
"production_endpoint": "https://api.stripe.com",
"methods": 42,
"auth": {
"type": "api_key",
"header": "Authorization"
},
"execution_policy": {
"max_retries": 5,
"on_401": "refresh_and_retry",
"max_concurrent": 3,
"idempotency": {
"mode": "dynamic"
}
}
}
}

How manifest.json Fits into Execution

Every API execution follows the same flow.

tooling.json
│
│ Is this tool trusted?
▼
policies.json
│
│ Is this request allowed?
▼
manifest.json
│
│ Which endpoint?
│ Which auth?
│ Retry policy?
│ Timeout?
│ Idempotency?
▼
Execute API Request

tooling.json defines what can execute.

policies.json defines whether it should execute.

manifest.json defines how it executes.

Together, these three files form the core execution model of Swytchcode.


Best Practices

  • Do not edit generated values unless necessary.
  • Commit manifest.json to version control.
  • Keep integration versions pinned.
  • Prefer sandbox mode during development.
  • Configure retries based on the API provider.
  • Enable idempotency for mutating operations.
  • Review timeout settings for long-running APIs.

Continue learning about the execution pipeline: