Markdown Editor

Developer and SDK Guide Template

Developer guide – setup, auth, multi-language quick start, errors, pagination, rate limits, webhooks, sandbox, versioning

Документація та підтримкаПовнийdeveloperguide

Як використовувати: Use alongside a generated API reference to help developers integrate. Write the quick example first and test it with a new account; then errors, limits and webhooks. The main pitfall is code samples that do not run as written.

Перегляд

Developer Guide: [Product name] API and SDKs

Complete guide for developers who integrate with an API or build on an SDK: concepts, setup, authentication, working code, error handling, limits, webhooks, testing, versioning and support. Based on common industry practice for developer documentation. Replace every [bracketed] value and every row marked Example.

Document control

FieldValue
Document ID[DG-PRODUCT-001]
API version[v2 (2026-09-01)]
SDK versions[Python 4.1, JavaScript 5.0, Go 1.8]
Guide version[0.1]
Status[Draft / In review / Approved / Superseded]
Owner[@devrel-owner]
Approvers[@api-lead], [@security-reviewer]
Last updatedYYYY-MM-DD

Approvals

RoleNameDecisionDate
API product owner[@name][Approved / Rejected / Approved with comments]YYYY-MM-DD
SDK maintainer[@name][Decision]YYYY-MM-DD
Security reviewer[@name][Decision]YYYY-MM-DD

Revision history

VersionDateAuthorChange summary
0.1YYYY-MM-DD[@name]Initial draft
1.0YYYY-MM-DD[@name]Example: Published with API v2 general availability
1.1YYYY-MM-DD[@name]Example: Added Go samples and webhook signature verification

Contents

1. Overview

The [Product name] API lets you [create and manage invoices, collect payments and receive status updates] from your own application. Official SDKs wrap the REST API for [Python, JavaScript and Go]; any language that can send HTTPS requests and parse JSON can use the API directly.

You canYou cannot
Example: Create, update and send invoicesChange account billing settings (dashboard only)
Example: Receive webhook events for payment statusAccess other accounts' data
[Capability][Limitation]

2. Concepts

ConceptMeaningExample
ResourceAn object with a stable ID and URLinv_8f2c... at /v2/invoices/inv_8f2c...
Idempotency keyA client-chosen key that makes a retried request safeIdempotency-Key: 6c1e-...
EnvironmentSeparate data and keys for testing and live usesandbox, live
EventA record that something happened, delivered by webhookinvoice.paid
ExpansionInline related objects in a response?expand=customer

The main objects relate as follows. Use this model to decide which IDs to store in your own database.

All timestamps are ISO 8601 in UTC. Monetary amounts are integers in the smallest currency unit (for example cents) with an ISO 4217 currency code.

3. Setup

  1. Create a developer account at [https://dashboard.example.com/signup].

  2. In Developers > API keys, create a sandbox secret key.

  3. Store it in an environment variable, never in source code:

    export EXAMPLE_API_KEY="sk_sandbox_[...]"
    
  4. Install an SDK:

    LanguageRequirementInstall
    Python3.10+pip install example-sdk==4.1.*
    JavaScript / TypeScriptNode.js 20+npm install @example/sdk@5
    Go1.22+go get github.com/example/[email protected]

3.1 Environments and base URLs

EnvironmentBase URLKeys start withData
Sandboxhttps://api.sandbox.example.com/v2sk_sandbox_Test data only; reset on request
Livehttps://api.example.com/v2sk_live_Real customers and money

The SDKs pick the base URL from the key prefix, so switching environments means switching keys. Keep separate keys per environment and per service so you can revoke one without affecting the others.

3.2 Recommended project setup

  • Read the key from configuration at start-up and fail fast if it is missing.
  • Create one client per process and reuse it; it keeps connections open.
  • Turn on SDK debug logging only in development: it prints request paths and status codes but never the key.

4. Quick example

Create a customer and an invoice, then print the invoice URL.

import os
from example_sdk import Client

client = Client(api_key=os.environ["EXAMPLE_API_KEY"])
customer = client.customers.create(email="[email protected]", name="Ana Silva")
invoice = client.invoices.create(
    customer=customer.id,
    currency="EUR",
    lines=[{"description": "Consulting, March", "amount": 120000}],
)
print(invoice.hosted_url)
import { Client } from "@example/sdk";

const client = new Client({ apiKey: process.env.EXAMPLE_API_KEY });
const customer = await client.customers.create({ email: "[email protected]", name: "Ana Silva" });
const invoice = await client.invoices.create({
  customer: customer.id,
  currency: "EUR",
  lines: [{ description: "Consulting, March", amount: 120000 }],
});
console.log(invoice.hostedUrl);
curl https://api.example.com/v2/invoices \
  -H "Authorization: Bearer $EXAMPLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"customer":"cus_123","currency":"EUR","lines":[{"description":"Consulting, March","amount":120000}]}'

Expected output: a URL such as https://pay.example.com/i/inv_8f2c.... Open it to see the invoice in the sandbox.

5. Authentication

MethodUse whenHeader
Secret API keyServer-to-server calls from your backendAuthorization: Bearer sk_live_...
OAuth 2.0 authorization code with PKCEActing on behalf of another accountAuthorization: Bearer <access token>
Restricted keyA service needs only some permissionsSame as secret key

Rules for keys:

  • Never ship a secret key in a mobile app or browser code.
  • Use restricted keys with the minimum scopes, for example invoices:write.
  • Rotate keys every [90] days and immediately if exposed; old keys can overlap with new ones for [24] hours.

For OAuth, redirect the account holder to https://connect.example.com/oauth/authorize with your client_id, the requested scope, a random state and a PKCE code_challenge. After consent, exchange the returned code at POST /oauth/token for an access token (valid [1 hour]) and a refresh token (valid [90 days], rotated on each use). Store refresh tokens encrypted and revoke them when the connected account disconnects your app.

A missing or invalid key returns 401 with error code authentication_failed.

6. Core tasks

6.1 Retrieve and update a resource

Retrieve an object by its ID, optionally expanding related objects, then send only the fields you want to change. Fields you omit keep their value; send null to clear an optional field.

invoice = client.invoices.retrieve("inv_8f2c", expand=["customer"])
client.invoices.update(invoice.id, due_date="2026-11-30")

6.2 Make a write request safe to retry

Send an idempotency key with every POST. If the network fails, retry with the same key; the API returns the original result instead of creating a duplicate.

await client.invoices.create(params, { idempotencyKey: crypto.randomUUID() });

6.3 Filter and expand

GET /v2/invoices?status=open&created[gte]=2026-10-01&expand=customer returns open invoices created since 1 October with the customer inline.

6.4 Attach your own data with metadata

Every resource accepts up to [50] metadata key-value pairs (keys up to 40 characters, values up to 500). Use metadata to store your own order ID so you can match webhook events to your records without an extra lookup. Do not store personal or sensitive data in metadata; it appears in exports and in the dashboard.

client.invoices.update("inv_8f2c", metadata={"order_id": "ORD-2026-00417"})

6.5 Search across resources

GET /v2/search/invoices?query=metadata['order_id']:'ORD-2026-00417' returns matching invoices. Search results can lag writes by up to [60 seconds], so use direct retrieval when you need the latest state.

7. Error handling

Errors use HTTP status codes and a JSON body:

{
  "error": {
    "type": "invalid_request",
    "code": "parameter_missing",
    "message": "The 'currency' parameter is required.",
    "param": "currency",
    "request_id": "req_91ab..."
  }
}
HTTP statusTypeMeaningRetry?
400invalid_requestMissing or invalid parameterNo; fix the request
401authentication_failedMissing, invalid or revoked keyNo
403permission_deniedKey lacks the scopeNo
404not_foundResource does not exist in this environmentNo
409conflictIdempotency key reused with different parametersNo
429rate_limitedToo many requestsYes, after Retry-After
500, 502, 503api_errorTemporary problem on our sideYes, with backoff

Retry with exponential backoff and jitter: wait tn=min⁡(tmax, t0⋅2n)⋅U(0.5,1)t_n = \min(t_{max},\ t_0 \cdot 2^n) \cdot U(0.5, 1) seconds before attempt nn, with t0=0.5t_0 = 0.5 and tmax=30t_{max} = 30. Always log request_id.

8. Pagination

List endpoints return up to limit items (default 20, maximum 100) and a cursor:

{ "data": [ ... ], "has_more": true, "next_cursor": "cur_3Jd..." }

Pass starting_after=<next_cursor> to get the next page. The SDKs iterate automatically:

for invoice in client.invoices.list(status="open").auto_paging_iter():
    process(invoice)

9. Rate limits

EnvironmentLimitBurst
Sandbox25 requests per second per account50
Live100 requests per second per account200

Every response includes RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. On 429, wait for the Retry-After seconds. Use webhooks instead of polling to stay well under the limit.

10. Webhooks

  1. Register an HTTPS endpoint in Developers > Webhooks and choose events.
  2. Respond with 2xx within 10 seconds; process work asynchronously.
  3. Verify the signature before trusting the payload:
from example_sdk.webhooks import verify

event = verify(payload=request.body,
               header=request.headers["Example-Signature"],
               secret=os.environ["EXAMPLE_WEBHOOK_SECRET"],
               tolerance_seconds=300)

Failed deliveries are retried for [3 days] with increasing delay. Events may arrive out of order or more than once; deduplicate by event.id and check the resource's current state before acting.

EventSent when
invoice.finalizedAn invoice can no longer be edited
invoice.paidPayment succeeded
invoice.payment_failedA payment attempt failed
[event][Trigger]

11. Testing and sandbox

The sandbox is fully isolated from live data. Use these test values:

ValueBehaviour
Card 4242 4242 4242 4242Payment succeeds
Card 4000 0000 0000 0002Payment is declined
Email ending [email protected]Invoice email bounces

Forward webhooks to your laptop with example-cli listen --forward-to localhost:3000/webhooks, and trigger events with example-cli trigger invoice.paid.

11.1 Go-live checklist

#CheckDone
1Live keys stored in the secret manager, not in code or CI logs[ ]
2Every write request sends an idempotency key[ ]
3Webhook signatures are verified and events deduplicated by ID[ ]
4429 and 5xx are retried with backoff; other errors are logged with request_id[ ]
5API version pinned in configuration[ ]
6Alerting on webhook delivery failures and error rate[ ]
7Tested against the sandbox scenarios in this section[ ]

12. SDK reference conventions

HTTPPythonJavaScriptGo
POST /v2/invoicesclient.invoices.create()client.invoices.create()client.Invoices.Create()
GET /v2/invoices/{id}client.invoices.retrieve(id)client.invoices.retrieve(id)client.Invoices.Get(id)
Field hosted_urlhosted_urlhostedUrlHostedURL

SDK errors raise or return typed exceptions named after the error type, for example RateLimitedError. Each SDK follows semantic versioning.

13. Versioning and deprecation

The API version is a date sent in the Example-Version header; your account default is set when you create it. Additive changes (new fields, new endpoints, new event types) can happen at any time, so parse JSON tolerantly. Breaking changes ship only in a new version, announced at least [12 months] before the old version is retired.

API versionReleasedStatusRetirement date
Example: 2026-09-01 (v2)2026-09-01CurrentNone planned
Example: 2024-03-15 (v1)2024-03-15Deprecated2027-09-30
[Version]YYYY-MM-DD[Current / Deprecated / Retired]YYYY-MM-DD

Calls to a deprecated version return a Deprecation header with the retirement date, so you can detect old integrations from your own logs.

14. Migration notes

v1v2Action
Example: amount as decimal stringamount as integer in minor unitsMultiply by 100 for EUR, USD
Example: Offset pagination (page)Cursor pagination (starting_after)Use SDK auto-paging
Example: Unsigned webhooksSigned webhooksAdd signature verification

Full details: [Migration Guide v1 to v2].

15. Best practices

  • Keep keys in a secret manager and use restricted keys per service.
  • Send an idempotency key with every write request.
  • Handle 429 and 5xx with backoff; never retry 4xx other than 429.
  • Use webhooks for state changes instead of polling.
  • Store our resource IDs in your database rather than matching on names.
  • Pin the API version and SDK major version; upgrade deliberately.
  • Set client timeouts (for example 10 s connect, 30 s read) so a slow network does not block your workers indefinitely.
  • Log request IDs, not request bodies, to avoid storing personal data and secrets in your logs.
  • Subscribe to the changelog feed so deprecations never surprise you.

16. Support

NeedWhere
API status and incidents[https://status.example.com]
Questions and examples[Developer forum URL]
Bugs in an SDKIssue tracker in the SDK repository
Account or production issues[[email protected]]

Include the request_id, API version, SDK name and version, and a minimal code sample. Never send secret keys.

Appendix A. Glossary

TermMeaning
Minor unitSmallest unit of a currency, for example cents for EUR
IdempotencyProperty that repeating a request has the same effect as sending it once
ScopeA named permission attached to a restricted key or OAuth token
CursorOpaque value that marks a position in a list for pagination
[Term][Meaning]

Standard and licence note

Based on common industry practice for API and SDK developer documentation (guides that combine concepts, quick starts, task examples and operational rules, with a separate generated API reference). This template is original text written for this collection. Template licence: MIT.

Схожі шаблони