diff --git a/app/docs/[[...slug]]/page.tsx b/app/docs/[[...slug]]/page.tsx index 5018862..4faa834 100644 --- a/app/docs/[[...slug]]/page.tsx +++ b/app/docs/[[...slug]]/page.tsx @@ -45,7 +45,7 @@ export default async function DocumentationPage({ params }: DocsPageProperties) const pageIndex = pages.findIndex((candidate) => candidate.url === page.url); const previous = neighbour(pageIndex > 0 ? pages[pageIndex - 1] : undefined); const next = neighbour(pageIndex >= 0 && pageIndex < pages.length - 1 ? pages[pageIndex + 1] : undefined); - const toc = page.data.toc.filter((item) => item.depth === 2); + const toc = page.data.toc.filter((item) => item.depth === 2 || item.depth === 3); const MdxContent = page.data.body; const githubUrl = `https://github.com/autoplus-gg/docs/edit/main/content/docs/${page.path}`; const markdownUrl = `${page.url}.md`; diff --git a/content/docs/public-api.mdx b/content/docs/public-api.mdx index 84872bc..3744f5a 100644 --- a/content/docs/public-api.mdx +++ b/content/docs/public-api.mdx @@ -1,8 +1,8 @@ --- title: Public API Documentation heading: "📄 Public API Documentation" -description: AutoPlus's API is based on a HTTPS/REST architecture. The API is there mostly to be used in the website autoplus.gg but some endpoints can be used to integrate AutoPlus in your own projects. -metadataTitle: "AutoPlus ― Public API Documentation" +description: Reference documentation for the AutoPlus public REST API and automated trade endpoints. +metadataTitle: "AutoPlus — Public API Documentation" navigationTitle: Public API Docs ogImage: /banner.png icon: file-code @@ -11,99 +11,402 @@ order: 5 ## API Reference [#api-reference] -AutoPlus's API is based on a HTTPS/REST architecture. The API is there mostly to be used in the website autoplus.gg but some endpoints can be used to integrate AutoPlus in your own projects. +The AutoPlus public API uses HTTPS and JSON. It provides public AutoPlus statistics and user-authenticated endpoints for opening and automating escrow trades. ### Base URL [#base-url] - {"https://api.autoplus.gg/v{version}"} + https://api.autoplus.gg/v1 -Where `{"{version}"}` is the API version you want to use. Currently, the only version available is v1. +Version `v1` is currently the only available API version. ### Authentication [#authentication] -Some endpoints require authentication. You can authenticate using a api key in the Authorization header. - -### Example: [#authentication-example] +Every trade endpoint requires an API token associated with one Discord user. Send it using the bearer authentication scheme: - Authorization: h27FGjb3AyjVkGBzRThH + Authorization: Bearer YOUR_API_TOKEN -Where `h27FGjb3AyjVkGBzRThH` is your api key. To get your API key, open a support ticket. +Open a support ticket in the [AutoPlus Discord server](https://discord.gg/autoplus) to request an API token. Keep it private and use it only from a trusted server. + +The `/servers`, `/stats`, and `/transactions` endpoints do not require authentication. + +### Request format [#request-format] + +Requests with a body must include `Content-Type: application/json`. + +Discord user IDs, guild IDs, and trade IDs are JSON strings. Timestamps are integer Unix timestamps in seconds. Optional response fields are omitted when they have no value. + +### Idempotency [#idempotency] + +The following endpoints require an `Idempotency-Key` header: + +- `POST /guilds/{guildId}/trades` +- `POST /trades/{id}/confirm-action` +- `POST /trades/{id}/confirm-claim` + +The key must contain 8 to 128 characters. Use a unique random value for each new operation. If a request times out, retry the identical request with the same key. Reusing a key with different request data returns `409 Conflict`. ### Crypto type [#crypto] -This refers to the crypto/token that the bot supports. Here is the exact type: +The `Crypto` type identifies both the asset and, for tokens, its network: "ltc" | "sol" | "eth" | "bnb" | "btc" | "pol" | "sol_usdt" | "sol_usdc" | "eth_usdt" | "eth_usdc" | "bnb_usdt" | "bnb_usdc" | "pol_usdt" | "pol_usdc" | "pol_usdce" -## Endpoints [#endpoints] +Availability also depends on the networks currently enabled by AutoPlus. + +### Trade automation rules [#trade-automation-rules] -Here are the available endpoints: +- The authenticated user must be the sender or receiver of every trade they access. +- Route parameter `id` is the Discord trade thread ID returned by trade creation, not the incremental `tradeId`. +- Identification and terms confirmations must repeat the exact values from the latest trade response. +- Only the sender can release funds. Only the receiver can return funds. +- Release, return, and claim operations use short-lived confirmation challenges. +- A successful claim confirmation returns `202 Accepted`; poll trade events for the final result. +- The API can provide a reduction return address when a trade is already in `waitingReduceAmountAddress`, but it cannot initiate a reduction. -## GET /servers [toc] [#servers-endpoint] +## Public Endpoints [#public-endpoints] + +### GET /servers [toc] [#servers-endpoint] -

Get the list of all servers the bot is in.

-

Response Structure

-

Each object in the array has the following structure:

+

Get the list of Discord servers AutoPlus is in.

+

Response Structure

+

Each object in the returned array has this structure:

-## GET /stats [toc] [#stats-endpoint] +### GET /stats [toc] [#stats-endpoint] -

Get the stats of the whole bot.

-

Response Structure

+

Get global AutoPlus trade statistics.

+

Response Structure

-## GET /transactions [toc] [#transactions-endpoint] +### GET /transactions [toc] [#transactions-endpoint] -

Get the 10 most recent transactions made on the bot.

-

Response Structure

-

Each object in the array has the following structure:

- crypto, description: "The crypto used in the transaction" }, - { field: "amount", type: "number", description: "The amount of crypto handled in the transaction" }, - { field: "amountUsd", type: "number", description: "The amount of crypto in USD handled in the transaction" }, - { field: "txid", type: "string", description: "The transaction ID of the bot receiving the funds" }, - { field: "txid2", type: "string", description: "The transaction ID of the bot releasing the funds" }, - { field: "receivedAt", type: "number", description: "The timestamp of when the bot received the funds, in seconds since unix epoch" }, - { field: "closedAt", type: "number", description: "The timestamp of when the bot closed the trade, in seconds since unix epoch" } +

Get the 10 most recent public AutoPlus transactions.

+

Response Structure

+

Each object in the returned array has this structure:

+ +
+ +## Authenticated Endpoints [#authenticated-endpoints] + +All endpoints in this section require bearer authentication. + +### POST /guilds/{"{guildId}"}/trades [toc] [#create-trade-endpoint] + + +

Create a trade in an API-enabled Discord server.

+

Requires bearer authentication and an Idempotency-Key header.

+

Request Structure

+ +

The authenticated user must be either sender or receiver. Both participants must be different, non-bot members of the guild.

+

Response Structure — 201 Created

+ +
+ +### GET /trades/{"{id}"} [toc] [#get-trade-endpoint] + + +

Get the current public representation of a trade.

+

Requires bearer authentication. The caller must be a participant.

+

Response — 200 OK

+

Returns a Trade. The escrow deposit address is exposed only to the sender.

+
+ +### GET /trades/{"{id}"}/events [toc] [#trade-events-endpoint] + + +

Poll durable trade events using a client-managed offset.

+

Requires bearer authentication. Maximum two concurrent polls per token and trade.

+

Query Parameters

+ +

Response Structure — 200 OK

+ +

An empty events array means the poll timed out without a new event. Repeat the same offset. If retained history expired, the API returns a current trade.snapshot.

+
+ +### PUT /trades/{"{id}"}/identification [toc] [#select-identification-endpoint] + + +

Select the authenticated user's role after identification was reset.

+

Valid only while the trade is waiting for identification.

+

Request Structure

+ +

Response — 200 OK

+

Returns the updated Trade.

+
+ +### POST /trades/{"{id}"}/identification/confirm [toc] [#confirm-identification-endpoint] + + +

Confirm the exact current sender and receiver assignment.

+

Request Structure

+ +

Response — 200 OK

+

Returns the updated Trade. After both participants confirm the same revision, the trade moves to waitingTermsAcceptance.

+
+ +### POST /trades/{"{id}"}/identification/reset [toc] [#reset-identification-endpoint] + + +

Clear both role assignments and their confirmations.

+

Request Structure

+ +

Response — 200 OK

+

Returns the updated Trade.

+
+ +### PUT /trades/{"{id}"}/terms [toc] [#set-terms-endpoint] + + +

Set, replace, or clear the authenticated user's terms.

+

Valid only while the trade is waiting for terms acceptance.

+

Request Structure

+ +

Each update creates a new terms version and clears existing confirmations.

+

Response — 200 OK

+

Returns the updated Trade.

+
+ +### POST /trades/{"{id}"}/terms/confirm [toc] [#confirm-terms-endpoint] + + +

Confirm the complete current terms exactly as returned by the API.

+

Request Structure

+ +

Response — 200 OK

+

Returns the updated Trade. After both participants confirm, the trade moves to waitingReceive. The sender's response then includes the escrow deposit address.

+
+ +### POST /trades/{"{id}"}/action [toc] [#request-action-endpoint] + + +

Request a short-lived confirmation challenge for releasing or returning funds.

+

Valid at waitingAction. Sender may release; receiver may return.

+

Request Structure

+ +

Response Structure — 201 Created

+ +
+ +### POST /trades/{"{id}"}/confirm-action [toc] [#confirm-action-endpoint] + + +

Confirm a release or return challenge.

+

Requires an Idempotency-Key header.

+

Request Structure

+ +

Response — 200 OK

+

Returns the updated Trade. Release moves the trade to waitingAddress; return moves it to waitingReturnAddress.

+
+ +### POST /trades/{"{id}"}/claim-funds [toc] [#claim-funds-endpoint] + + +

Request a confirmation challenge for a payout, return, or reduction-return address.

+

Receiver claims at waitingAddress. Sender claims at waitingReturnAddress or waitingReduceAmountAddress.

+

Request Structure

+ +

Response Structure — 201 Created

+
-## POST /guilds/{"{guildId}"}/trades [toc] [#open-trade-endpoint] +### POST /trades/{"{id}"}/confirm-claim [toc] [#confirm-claim-endpoint] - -

Open a trade with the bot.

-

Request Structure

-

This request requires authentication.

+ +

Confirm the destination and begin the fund transfer.

+

Requires an Idempotency-Key header.

+

Request Structure

crypto, description: "The crypto used in the transaction" } + { field: "address", type: "string", description: "Exact original address returned by the challenge." }, + { field: "crypto", type: "Crypto", description: "Exact crypto returned by the challenge." }, + { field: "confirmationCode", type: "string", description: "Confirmation code returned by the challenge." }, + { field: "btcFee?", type: "BtcFee", description: "One unchanged BTC fee option. Omit for non-BTC trades." } ]} /> -

Response Structure

+

Response Structure — 202 Accepted

+

Continue polling trade events. trade.fund.execution.updated reports execution progress and trade.closed reports the final release or return.

+ +## Response Structures [#response-structures] + +### Trade [#trade-structure] + + + +### TradeStatus [#trade-status-structure] + + + "waitingIdentification" | "waitingTermsAcceptance" | "waitingReceive" | "waitingConfirmations" | "waitingAction" | "waitingReduceAmountAddress" | "waitingReturnAddress" | "waitingAddress" | "completed" + + +### TradeTerms [#trade-terms-structure] + + + +Each `TradeTermsSide` contains: + + + +### TradeEvent [#trade-event-structure] + + + + + "trade.snapshot" | "trade.created" | "trade.identification.updated" | "trade.terms.updated" | "trade.status.changed" | "trade.fund.execution.updated" | "trade.support.updated" | "trade.closed" + + +Events are retained for at least one hour. A closed trade may no longer be returned by `GET /trades/{id}`, but its retained event stream remains available to its participants. + +### BTC fee types [#btc-fee-structure] + + + +## Errors [#errors] + +API errors use the HTTP status code as the primary result and return: + + + +Common status codes: + + + +On `stale_revision` or `confirmation_mismatch`, fetch the trade again and compare the new values before asking the user to confirm them. Never automatically confirm changed roles, terms, recipients, crypto, or addresses. diff --git a/src/components/DocsComponents.tsx b/src/components/DocsComponents.tsx index 718db71..615e4fb 100644 --- a/src/components/DocsComponents.tsx +++ b/src/components/DocsComponents.tsx @@ -146,11 +146,15 @@ export function Endpoint({ id }: { children: ReactNode; - method: "post" | "get"; + method: "post" | "get" | "put"; endpoint: string; id: string; }) { - const methodColor = method === "post" ? "border-green-800/30 bg-green-900/20" : "border-blue-800/30 bg-blue-900/20"; + const methodColor = { + get: "border-blue-800/30 bg-blue-900/20", + post: "border-green-800/30 bg-green-900/20", + put: "border-amber-800/30 bg-amber-900/20" + }[method]; return (
diff --git a/src/components/TableOfContents.tsx b/src/components/TableOfContents.tsx index 15f57ba..49eed87 100644 --- a/src/components/TableOfContents.tsx +++ b/src/components/TableOfContents.tsx @@ -2,10 +2,11 @@ import type { TOCItemType } from "fumadocs-core/toc"; import Link from "next/link"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; export function TableOfContents({ toc }: { toc: TOCItemType[] }) { const [activeSection, setActiveSection] = useState(""); + const scrollAreaRef = useRef(null); useEffect(() => { const handleHashChange = () => setActiveSection(window.location.hash.slice(1)); @@ -35,19 +36,48 @@ export function TableOfContents({ toc }: { toc: TOCItemType[] }) { return () => observer.disconnect(); }, [toc]); + useEffect(() => { + const container = scrollAreaRef.current; + if (!container || !activeSection) return; + + const activeLink = Array.from(container.querySelectorAll("[data-toc-id]")).find( + (link) => link.dataset.tocId === activeSection + ); + if (!activeLink) return; + + const itemTop = activeLink.offsetTop; + const itemBottom = itemTop + activeLink.offsetHeight; + const viewTop = container.scrollTop; + const viewBottom = viewTop + container.clientHeight; + if (itemTop >= viewTop && itemBottom <= viewBottom) return; + + container.scrollTo({ + top: itemTop - (container.clientHeight - activeLink.offsetHeight) / 2, + behavior: "smooth" + }); + }, [activeSection]); + return ( -
-
-
- {toc.map((item) => { +
+
+
+ {toc.map((item, index) => { const id = item.url.slice(1); + const nested = item.depth === 3; + const active = activeSection === id; return ( 0 ? "mt-2 " : ""}font-medium` + } ${active ? "text-primary" : "transition-colors duration-200 hover:text-primary"}`} > {item.title}