# Integrating Jupiter

> Comprehensive guidance for integrating Jupiter APIs (Swap, Lend, Perps, Trigger, Recurring, Tokens, Price, Portfolio, Prediction Markets, Send, Studio, Lock, Routing). Use for endpoint selection, integration flows, error handling, and production hardening.

- **Type:** Skill
- **Install:** `agentstack add skill-jup-ag-agent-skills-integrating-jupiter`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [jup-ag](https://agentstack.voostack.com/s/jup-ag)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [jup-ag](https://github.com/jup-ag)
- **Source:** https://github.com/jup-ag/agent-skills/tree/main/skills/integrating-jupiter
- **Website:** https://jup.md

## Install

```sh
agentstack add skill-jup-ag-agent-skills-integrating-jupiter
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Jupiter API Integration

Single skill for all Jupiter APIs, optimized for fast routing and deterministic execution.

**Base URL**: `https://api.jup.ag`
**Auth**: `x-api-key` from [developers.jup.ag](https://developers.jup.ag/) (**required for Jupiter REST endpoints**)

## Use/Do Not Use

Use when:
- The task requires choosing or calling Jupiter endpoints.
- The task involves swap, lending, perps, orders, pricing, portfolio, send, studio, lock, or routing.
- The user needs debugging help for Jupiter API calls.

Do not use when:
- The task is generic Solana setup with no Jupiter API usage.
- The task is UI-only with no API behavior decisions.
- The agent context is not DeFi/crypto (generic triggers like `buy`, `sell`, `trade` assume a DeFi domain).

**Triggers**: `swap`, `quote`, `gasless`, `best route`, `buy`, `sell`, `trade`, `convert`, `token exchange`, `jupiter api`, `jup.ag`, `ultra`, `metis`, `ultra swap`, `ultra api`, `ultra-api.jup.ag`, `lend`, `borrow`, `earn`, `yield`, `apy`, `deposit`, `liquidation`, `perps`, `leverage`, `long`, `short`, `position`, `futures`, `margin trading`, `limit order`, `trigger`, `price condition`, `dca`, `recurring`, `scheduled swaps`, `token metadata`, `token search`, `verification`, `shield`, `price`, `valuation`, `price feed`, `portfolio`, `positions`, `holdings`, `prediction markets`, `market odds`, `event market`, `invite transfer`, `send`, `clawback`, `create token`, `studio`, `claim fee`, `vesting`, `distribution lock`, `unlock schedule`, `dex integration`, `rfq integration`, `routing engine`, `status page`, `health check`, `service health`, `accumulate`, `auto-buy`

## Developer Quickstart

```typescript
import { Connection, Keypair, VersionedTransaction } from '@solana/web3.js';

const API_KEY = process.env.JUPITER_API_KEY!;  // from developers.jup.ag
if (!API_KEY) throw new Error('Missing JUPITER_API_KEY');
const BASE = 'https://api.jup.ag';
const headers = { 'x-api-key': API_KEY };

async function jupiterFetch(path: string, init?: RequestInit): Promise {
  const res = await fetch(`${BASE}${path}`, {
    ...init,
    headers: { ...headers, ...init?.headers },
  });
  if (res.status === 429) throw { code: 'RATE_LIMITED', retryAfter: Number(res.headers.get('Retry-After')) || 10 };
  if (!res.ok) {
    const raw = await res.text();
    let body: any = { message: raw || `HTTP_${res.status}` };
    try {
      body = raw ? JSON.parse(raw) : body;
    } catch {
      // keep text fallback body
    }
    throw { status: res.status, ...body };
  }
  return res.json();
}

// Sign and send any Jupiter transaction
async function signAndSend(
  txBase64: string,
  wallet: Keypair,
  connection: Connection,
  additionalSigners: Keypair[] = []
): Promise {
  const tx = VersionedTransaction.deserialize(Buffer.from(txBase64, 'base64'));
  tx.sign([wallet, ...additionalSigners]);
  const sig = await connection.sendRawTransaction(tx.serialize(), {
    maxRetries: 0,
    skipPreflight: true,
  });
  return sig;
}
```

## Intent Router (first step)

| User intent | API family | First action |
|---|---|---|
| Swap/quote | [Swap](#swap) | `GET /swap/v2/order` -> sign -> `POST /swap/v2/execute` |
| Lend/borrow/yield | [Lend](#lend) | `POST /lend/v1/earn/deposit` or `/withdraw` |
| Leverage/perps | [Perps](#perps) | On-chain via Anchor IDL (no REST API yet) |
| Limit orders | [Trigger](#trigger-limit-orders) | JWT auth -> `POST /trigger/v2/orders/price` |
| DCA/recurring buys | [Recurring](#recurring-dca) | `POST /recurring/v1/createOrder` -> sign -> `POST /recurring/v1/execute` |
| Token search | [Tokens](#tokens) | `GET /tokens/v2/search?query={mint}` |
| Token verification/metadata update | Use `jupiter-vrfd` skill | Defer — not handled by this skill |
| Price lookup | [Price](#price) | `GET /price/v3?ids={mints}` |
| Portfolio/positions | [Portfolio](#portfolio) | `GET /portfolio/v1/positions/{address}` |
| Prediction market integration | [Prediction Markets](#prediction-markets) | `GET /prediction/v1/events` -> `POST /prediction/v1/orders` |
| Invite send/clawback | [Send](#send) | `POST /send/v1/craft-send` -> sign -> send to RPC |
| Token creation/fees | [Studio](#studio) | `POST /studio/v1/dbc-pool/create-tx` -> upload -> submit |
| Vesting/distribution | [Lock](#lock) | On-chain program `LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn` |
| DEX/RFQ integration | [Routing](#routing) | Choose DEX (AMM trait) vs RFQ (webhook) path |

## API Playbooks

Use each block as a minimal execution contract. Fetch the linked refs for full request/response shapes, TypeScript interfaces, and parameter details.

### Swap

- **Base URL**: `https://api.jup.ag/swap/v2`
- **Triggers**: `swap`, `quote`, `gasless`, `best route`
- **Fee**: Variable by pair — 0 bps (Jupiter tokens/pegged), 2 bps (SOL-Stable), 5 bps (LST-Stable), 10 bps (most pairs), 50 bps (tokens ` (order mutations). JWT obtained via challenge-response: `POST /auth/challenge` → sign challenge with wallet → `POST /auth/verify` → receive token. JWT expiry does NOT affect open orders — they continue executing.
- **Endpoints**: `/auth/challenge` (POST, body: `walletPubkey` + `type`), `/auth/verify` (POST, body: `type` + `walletPubkey` + base58 `signature`), `/vault` (GET), `/vault/register` (GET), `/deposit/craft` (POST), `/orders/price` (POST create, PATCH update), `/orders/price/cancel/{orderId}` (POST, initiates withdrawal), `/orders/price/confirm-cancel/{orderId}` (POST, submits signed withdrawal + `cancelRequestId`), `/orders/history` (GET, wallet implicit via JWT)
- **Order types**: `single` (one directional trigger), `oco` (take-profit + stop-loss pair), `otoco` (entry trigger + OCO). `triggerCondition`: `"above"` or `"below"`.
- **Architecture**: Off-chain custodial vault (Privy) per wallet. Orders invisible on-chain until execution — MEV-resistant. Triggers on USD price (not pool rate ratios). Partial fills supported.
- **Gotchas**:
  - Order creation is 3 steps — `GET /vault/register` (register if new; returns `409 "Vault already registered"` if it exists, which is fine), `POST /deposit/craft` (returns `transaction` + `requestId`; the body MUST include `orderType: "price"` and `orderSubType` (`single`/`oco`/`otoco`)), sign deposit tx, then `POST /orders/price` with `depositRequestId` + `depositSignedTx`.
  - Cancellation is two-step — `POST /cancel/{orderId}` returns `transaction` + `requestId`; sign, then `POST /confirm-cancel/{orderId}` with `signedTransaction` + `cancelRequestId`.
  - Create response field is `id` (not `orderId`); order history objects use `orderState`/`rawState` (no `status` field).
- Refs: [Overview](https://developers.jup.ag/docs/trigger/index.md) | [Authentication](https://developers.jup.ag/docs/trigger/authentication.md) | [Create order](https://developers.jup.ag/docs/trigger/create-order.md) | [Order history](https://developers.jup.ag/docs/trigger/order-history.md) | [Manage orders](https://developers.jup.ag/docs/trigger/manage-orders.md) | [OpenAPI](https://developers.jup.ag/docs/openapi-spec/trigger/v2/trigger.yaml)

---

### Recurring (DCA)

- **Base URL**: `https://api.jup.ag/recurring/v1`
- **Triggers**: `dca`, `recurring`, `scheduled swaps`
- **Fee**: 0.1% on all recurring orders
- **Constraints**: Min 100 USD total, min 2 orders, min 50 USD per order
- **Pagination**: 10 orders per page
- **Endpoints**: `/createOrder` (POST), `/cancelOrder` (POST), `/execute` (POST), `/getRecurringOrders` (GET)
- **Gotchas**: Token-2022 NOT supported. Use `params.time` for order scheduling; price-based ordering is not supported.
- Refs: [Overview](https://developers.jup.ag/docs/recurring/index.md) | [Create](https://developers.jup.ag/docs/recurring/create-order.md) | [Get orders](https://developers.jup.ag/docs/recurring/get-recurring-orders.md) | [Best Practices](https://developers.jup.ag/docs/recurring/best-practices) | [OpenAPI](https://developers.jup.ag/docs/openapi-spec/recurring/recurring.yaml)

---

### Tokens

- **Base URL**: `https://api.jup.ag/tokens/v2`
- **Triggers**: `token metadata`, `token search`, `shield`
- **Endpoints**: `/search?query={q}` (GET, comma-separate mints, max 100), `/tag?query={tag}` (GET, `verified` or `lst`), `/{category}/{interval}` (GET, categories: `toporganicscore`, `toptraded`, `toptrending`; intervals: `5m`, `1h`, `6h`, `24h`), `/recent` (GET)
- **Gotchas**:
  - Use mint address as primary identity; treat symbol/name as convenience.
  - Primary trust signal is top-level `isVerified` (boolean) plus `organicScore` (0-100) and `organicScoreLabel` (`high`/`medium`/`low`).
  - Secondary risk flag: **`audit.isSus`** — a boolean present ONLY when `true` (a safe token has no `isSus` key at all), so check it defensively as `token.audit?.isSus === true`, never read it directly. Verified live on flagged tokens (dev holds ~100% of supply); those also carry `isVerified: null` and `organicScoreLabel: "low"`. Absence of `isSus` is NOT proof of safety — not all risky tokens carry the flag.
  - Other conditional `audit` fields the live API returns: `mintAuthorityDisabled`, `freezeAuthorityDisabled`, `topHoldersPercentage`, `devBalancePercentage`, `devMigrations`, `devMints` (all nullable, any may be absent; `devMigrations` is returned but missing from the current OpenAPI schema).
- Refs: [Overview](https://developers.jup.ag/docs/tokens/index.md) | [Token info v2](https://developers.jup.ag/docs/tokens/token-information.md) | [OpenAPI](https://developers.jup.ag/docs/openapi-spec/tokens/v2/tokens.yaml)

---

### Price

- **Base URL**: `https://api.jup.ag/price/v3`
- **Triggers**: `price`, `valuation`, `price feed`
- **Limit**: Max 50 mint IDs per request
- **Endpoints**: `/price/v3?ids={mints}` (GET, comma-separated)
- **Response**: keyed by mint, each value has `usdPrice`, `blockId`, `decimals`, `priceChange24h`, `liquidity`, `createdAt` (some tokens add conditional fields like `launchpad`, `stockData`, `scaledUiConfig`). Price API V3 has NO `confidenceLevel` field (that was V2).
- **Gotchas**: Tokens with unreliable pricing are omitted from the response entirely (not an error, no `null` placeholder). Fail closed when a requested mint is missing from the response for safety-sensitive actions. Use `blockId` to check price recency.
- Refs: [Overview](https://developers.jup.ag/docs/price/index.md) | [OpenAPI](https://developers.jup.ag/docs/openapi-spec/price/v3/price.yaml)

---

### Portfolio

- **Base URL**: `https://api.jup.ag/portfolio/v1`
- **Status**: Beta — Jupiter platforms only
- **Triggers**: `portfolio`, `positions`, `holdings`
- **Endpoints**: `/positions/{address}` (GET), `/positions/{address}?platforms={ids}` (GET), `/platforms` (GET), `/staked-jup/{address}` (GET)
- **Gotchas**: Treat empty positions as valid state. Response is beta — normalize into stable internal schema. Element types: `multiple`, `liquidity`, `trade`, `leverage`, `borrowlend`.
- Refs: [Overview](https://developers.jup.ag/docs/portfolio/index.md) | [Jupiter positions](https://developers.jup.ag/docs/portfolio/jupiter-positions.md) | [OpenAPI](https://developers.jup.ag/docs/openapi-spec/portfolio/portfolio.yaml)

---

### Prediction Markets

- **Base URL**: `https://api.jup.ag/prediction/v1`
- **Status**: Beta (breaking changes possible)
- **Geo-restricted**: US and South Korea IPs blocked
- **Price convention**: 1,000,000 native units = $1.00 USD
- **Triggers**: `prediction markets`, `market odds`, `event market`
- **Deposit mints**: JupUSD (`JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD`), USDC
- **Min order**: $5 (5,000,000 native units)
- **Endpoints**: `/events` (GET, returns `{data, pagination}`), `/events/search` (GET), `/markets/{marketId}` (GET), `/orderbook/{marketId}` (GET, returns `{yes, no, yes_dollars, no_dollars}`), `/orders` (POST, requires `isBuy` boolean + `ownerPubkey`), `/orders/status/{orderPubkey}` (GET), `/positions` (GET, `?ownerPubkey=`), `/positions/{positionPubkey}` (DELETE), `/positions/{positionPubkey}/claim` (POST), `/history` (GET), `/leaderboards` (GET)
- **Gotchas**:
  - Check `position.claimable` before claiming. Winners get $1/contract.
  - Markets are FLAT — fields like `provider`, `marketId`, `result`, `resolveAt`, `outcomes`, `clobTokenIds` live at the top level, not nested under `metadata`.
  - Contracts are fractional: use `contractsMicro` (1,000,000 = 1 contract) or `contractsDecimal`, not the legacy whole-number `contracts`.
- Refs: [Overview](https://developers.jup.ag/docs/prediction/index.md) | [Events](https://developers.jup.ag/docs/prediction/events-and-markets.md) | [Positions](https://developers.jup.ag/docs/prediction/open-positions.md) | [OpenAPI](https://developers.jup.ag/docs/openapi-spec/prediction/prediction.yaml)

---

### Send

- **Base URL**: `https://api.jup.ag/send/v1`
- **Status**: Beta
- **Triggers**: `invite transfer`, `send`, `clawback`
- **Supported tokens**: SOL, USDC, memecoins
- **Endpoints**: `/craft-send` (POST), `/craft-clawback` (POST), `/pending-invites` (GET), `/invite-history` (GET)
- **Gotchas**: **Dual-sign requirement** — sender + recipient keypair (derived from invite code). Claims only via Jupiter Mobile (no API claiming). Never expose invite codes.
- Refs: [Overview](https://developers.jup.ag/docs/send/index.md) | [Invite code](https://developers.jup.ag/docs/send/invite-code.md) | [Craft send](https://developers.jup.ag/docs/send/craft-send.md) | [OpenAPI](https://developers.jup.ag/docs/openapi-spec/send/send.yaml)

---

### Studio

- **Base URL**: `https://api.jup.ag/studio/v1`
- **Status**: Beta
- **Triggers**: `create token`, `studio`, `claim fee`
- **Endpoints**: `/dbc-pool/create-tx` (POST), `/dbc-pool/submit` (POST, multipart/form-data), `/dbc-pool/addresses/{mint}` (GET), `/dbc/fee` (POST), `/dbc/fee/create-tx` (POST)
- **Flow**: create-tx -> upload image to presigned URL -> upload metadata to presigned URL -> sign -> submit via `/dbc-pool/submit`
- **Gotchas**: Must submit via `/dbc-pool/submit` (not externally) for token to get a Studio page on jup.ag. Error codes: `403` = not authorized for pool, `404` = proxy account not found.
- Refs: [Overview](https://developers.jup.ag/docs/studio/index.md) | [Create token](https://developers.jup.ag/docs/studio/create-token.md) | [Claim fee](https://developers.jup.ag/docs/studio/claim-fee.md) | [OpenAPI](https://developers.jup.ag/docs/openapi-spec/studio/studio.yaml)

---

### Lock

- **Program ID**: `LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn`
- **Triggers**: `vesting`, `distribution lock`, `unlock schedule`
- **Integration**: On-chain program only (no REST API)
- **Source**: [github.com/jup-ag/jup-lock](https://github.com/jup-ag/jup-lock)
- **UI**: [lock.jup.ag](https://lock.jup.ag/)
- **Security**: Audited by OtterSec and Sec3
- **Gotchas**: No REST API. Use instruction scripts from the repo's `cli/src/bin/instructions` directory.
- Refs: [Lock overview](https://developers.jup.ag/docs/lock/index.md)

---

### Routing

- **Triggers**: `dex integration`, `rfq integration`, `routing engine`
- **Engines**: Juno (meta-aggregator), Metis (multi-hop DEX routing, powers the Swap API; formerly called Iris), JupiterZ (RFQ market maker quotes)
- **DEX Integration** (into Metis): Free, no fees. Prereqs: code health, security audit, market traction. Implement `jupiter-amm-interface` crate. **Critical**: No network calls in implementation (accounts are pre-batched and cached). Ref impl: [github.com/jup-ag/rust-amm-implementation](https://github.com/jup-ag/rust-amm-implementation)
- **RFQ Integration** (JupiterZ): Market makers host webhook at `/jupiter/rfq/quote` (POST, 250ms), `/jupiter/rfq/swap` (POST), `/jupiter/rfq/tokens` (GET). Reqs: 95% fill rate, 250ms response, 55s expiry. SDK: [github.com/jup-ag/rfq-webhook-toolkit](https://github.com/jup-ag/rfq-webhook-toolkit)
- **Market Listing**: Instant routing for tokens  {
  ok: boolean;
  result?: T;
  error?: { code: string | number; message: string; retryable: boolean };
}

async function jupiterAction(action: () => Promise): Promise> {

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [jup-ag](https://github.com/jup-ag)
- **Source:** [jup-ag/agent-skills](https://github.com/jup-ag/agent-skills)
- **License:** MIT
- **Homepage:** https://jup.md

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-jup-ag-agent-skills-integrating-jupiter
- Seller: https://agentstack.voostack.com/s/jup-ag
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
